Exemplo n.º 1
0
class MyMod:
    in_port = Input(dim(8))
    out0 = Output(dim(5))
    out1 = Output(dim(5))

    @generator
    def construct(mod):
        # Partial lower slice
        mod.out0 = mod.in_port[3:]
        # partial upper slice
        mod.out1 = mod.in_port[:5]
Exemplo n.º 2
0
class FSMUser:
    a = Input(types.i1)
    b = Input(types.i1)
    c = Input(types.i1)
    clk = Input(types.i1)
    is_a = Output(types.i1)
    is_b = Output(types.i1)
    is_c = Output(types.i1)

    @generator
    def construct(ports):
        fsm = FSM(a=ports.a, b=ports.b, c=ports.c, clk=ports.clk)
        ports.is_a = fsm.is_A
        ports.is_b = fsm.is_B
        ports.is_c = fsm.is_C
Exemplo n.º 3
0
class Slicing:
  In = Input(dim(8, 4, 5))
  Sel8 = Input(types.i8)
  Sel2 = Input(types.i2)

  OutIntSlice = Output(types.i2)
  OutArrSlice8 = Output(dim(8, 4, 2))
  OutArrSlice2 = Output(dim(8, 4, 2))

  @generator
  def create(ports):
    i = ports.In[0][0]
    ports.OutIntSlice = i.slice(ports.Sel2, 2)
    ports.OutArrSlice2 = ports.In.slice(ports.Sel2, 2)
    ports.OutArrSlice8 = ports.In.slice(ports.Sel8, 2)
Exemplo n.º 4
0
class M5:
    in0 = Input(dim(types.i32, 16))
    in1 = Input(types.i32)
    t_c = dim(types.i32, 16)
    c = Output(t_c)

    @generator
    def build(ports):
        # a 32x32xi1 ndarray.
        # A dtype of i32 is fairly expensive wrt. the size of the output IR, but
        # but allows for assigning indiviudal bits.
        m = NDArray((32, 32), dtype=types.i1, name='m1')

        # Assign individual bits to the first 32 bits.
        for i in range(32):
            m[0, i] = hw.ConstantOp(types.i1, 1)

        # Fill the next 15 values with an i32. The ndarray knows how to convert
        # from i32 to <32xi1> to comply with the ndarray dtype.
        for i in range(1, 16):
            m[i] = ports.in1

        # Fill the upportmost 16 rows with the input array of in0 : 16xi32
        m[16:32] = ports.in0

        # We don't provide a method of reshaping the ndarray wrt. its dtype.
        # that is: 32x32xi1 => 32xi32
        # This has massive overhead in the generated IR, and can be easily
        # achieved by a bitcast.
        ports.c = hw.BitcastOp(M5.t_c, m.to_circt())
Exemplo n.º 5
0
class M1:
    in1 = Input(dim(types.i32, 4, 8))
    out = Output(dim(types.i32, 2, 16))

    @generator
    def build(ports):
        ports.out = ports.in1.transpose((1, 0)).reshape((16, 2))
Exemplo n.º 6
0
class M1:
    in1 = Input(dim(types.i32, 4, 8))
    out = Output(dim(types.i32, 8, 4))

    @generator
    def build(ports):
        ports.out = ports.in1.transpose((1, 0))
Exemplo n.º 7
0
class M1:
    in1 = Input(dim(types.i32, 10))
    out = Output(dim(types.i32, 10))

    @generator
    def build(ports):
        ports.out = ports.in1.roll(3)
Exemplo n.º 8
0
class WireNames:
    clk = Input(types.i1)
    sel = Input(types.i2)
    data_in = Input(dim(32, 3))

    a = Output(types.i32)
    b = Output(types.i32)

    @generator
    def build(ports):
        foo = ports.data_in[0]
        foo.name = "foo"
        arr_data = dim(32, 4)([1, 2, 3, 4], "arr_data")
        ports.set_all_ports({
            'a': foo.reg(ports.clk).reg(ports.clk),
            'b': arr_data[ports.sel],
        })
Exemplo n.º 9
0
class M1:
    in1 = Input(dim(types.i32, 10))
    out = Output(dim(types.i32, 10))

    @generator
    def build(ports):
        m = NDArray(from_value=ports.in1, name='m1')
        ports.out = m.to_circt(create_wire=False)
Exemplo n.º 10
0
class Plus:
    a = Input(types.i32)
    b = Input(types.i32)
    y = Output(types.i32)

    def __init__(self, name: str = None) -> None:
        if name is not None:
            self.instance_name = name
Exemplo n.º 11
0
class ComplexPorts:
    clk = Input(types.i1)
    sel = Input(types.i2)
    data_in = Input(dim(32, 3))
    struct_data_in = Input(types.struct({"foo": types.i36}))

    a = Output(types.i32)
    b = Output(types.i32)
    c = Output(types.i32)

    @generator
    def build(ports):
        assert len(ports.data_in) == 3
        ports.set_all_ports({
            'a': ports.data_in[0].reg(ports.clk).reg(ports.clk),
            'b': ports.data_in[ports.sel],
            'c': ports.struct_data_in.foo[:-4]
        })
Exemplo n.º 12
0
class TopLevel:
    x = Input(types.i32)
    y = Output(types.i32)

    @generator
    def construct(mod):
        TopLevel.part1 = DesignPartition("part1")
        Plus("Plus1", a=mod.x, b=mod.x, partition=TopLevel.part1)
        mod.y = PlusPipeline(a=mod.x).y
Exemplo n.º 13
0
class PlusPipeline:
    a = Input(types.i32)
    y = Output(types.i32)

    @generator
    def construct(mod):
        p1 = Plus(a=mod.a, b=mod.a)
        p2 = Plus(a=p1.y, b=mod.a, partition=TopLevel.part1)
        p3 = Plus(a=p2.y, b=mod.a)
        mod.y = p3.y
Exemplo n.º 14
0
class M1:
    out = Output(dim(types.i32, 3, 3))

    @generator
    def build(ports):
        m = NDArray((3, 3), dtype=types.i32)
        for i in range(3):
            for j in range(3):
                m[i][j] = types.i32(i * 3 + j)
        ports.out = m.to_circt()
Exemplo n.º 15
0
class CompReg:
    clk = Clock()
    input = Input(types.i8)
    output = Output(types.i8)

    @generator
    def build(ports):
        with ports.clk:
            compreg = ports.input.reg(name="reg", sv_attributes=["dont_merge"])
            compreg.appid = AppID("reg", 0)
            ports.output = compreg
Exemplo n.º 16
0
class ComplexMux:

  Clk = Input(dim(1))
  In = Input(dim(3, 4, 5))
  Sel = Input(dim(1))
  Out = Output(dim(3, 4))
  OutArr = Output(dim(3, 4, 2))
  OutSlice = Output(dim(3, 4, 3))
  OutInt = Output(types.i1)

  @generator
  def create(ports):
    clk = ports.Clk
    select_from = Value([ports.In[3].reg(clk).reg(clk, cycles=2), ports.In[1]])
    ports.Out = select_from[ports.Sel]

    ports.OutArr = array_from_tuple(ports.In[0], ports.In[1])
    ports.OutSlice = ports.In[0:3]

    ports.OutInt = ports.In[0][0][ports.Sel]
Exemplo n.º 17
0
class Mux:
    clk = Clock()
    data = Input(dim(8, 14))
    sel = Input(types.i4)

    out = Output(types.i8)

    @generator
    def build(ports):
        sel_reg = ports.sel.reg()
        ports.out = ports.data.reg()[sel_reg].reg()
Exemplo n.º 18
0
class M1:
    in1 = Input(dim(types.i32, 10))
    in2 = Input(dim(types.i32, 10))
    in3 = Input(dim(types.i32, 10))
    out = Output(dim(types.i32, 30))

    @generator
    def build(ports):
        # Explicit ndarray.
        m = NDArray(from_value=ports.in1, name='m1')
        # Here we could do a sequence of transformations on 'm'...
        # Finally, concatenate [in2, m, in3]
        ports.out = ports.in2.concatenate((m, ports.in3))
Exemplo n.º 19
0
class CompReg:
    clk = Input(types.i1)
    input = Input(types.i8)
    output = Output(types.i8)

    @generator
    def build(ports):
        compreg = seq.CompRegOp(types.i8,
                                clk=ports.clk,
                                input=ports.input,
                                name="reg",
                                sym_name="reg")
        ports.output = compreg
Exemplo n.º 20
0
class M2:
    in0 = Input(dim(types.i32, 16))
    in1 = Input(types.i32)
    t_c = dim(types.i32, 8, 4)
    c = Output(t_c)

    @generator
    def build(ports):
        # a 32xi32 ndarray.
        m = NDArray((32, ), dtype=types.i32, name='m2')
        for i in range(16):
            m[i] = ports.in1
        m[16:32] = ports.in0
        m = m.reshape((4, 8))
        ports.c = m.to_circt()
Exemplo n.º 21
0
    class Mod:
        inp = Input(dim(SIZE))
        out = Output(dim(SIZE))

        @generator
        def construct(mod):
            c1 = hw.ConstantOp(dim(SIZE), 1)
            # CHECK: %[[EQ:.+]] = comb.icmp eq
            eq = comb.EqOp(c1, mod.inp)
            # CHECK: %[[A1:.+]] = hw.array_create %[[EQ]], %[[EQ]]
            a1 = hw.ArrayCreateOp([eq, eq])
            # CHECK: %[[A2:.+]] = hw.array_create %[[EQ]], %[[EQ]]
            a2 = hw.ArrayCreateOp([eq, eq])
            # CHECK: %[[COMBINED:.+]] = hw.array_concat %[[A1]], %[[A2]]
            combined = hw.ArrayConcatOp(a1, a2)
            mod.out = hw.BitcastOp(dim(SIZE), combined)
Exemplo n.º 22
0
def fsm_wrapper_class(fsm_mod, fsm_name, clock, reset=None):
    """
  Generate a wrapper class for the FSM class which contains the clock and reset
  signals, as well as a `fsm.hw_instance` instaitiation of the FSM.
  """
    class fsm_hw_mod:
        @generator
        def construct(ports):
            in_ports = {
                port_name: getattr(ports, port_name)
                for (port_name, _) in fsm_mod._pycde_mod.input_ports
            }
            fsm_instance = fsm_mod(**in_ports)
            # Attach clock and optional reset on the backedges created during
            # the MachineOp:instatiate call.
            clock_be = getattr(fsm_instance._instantiation, '_clock_backedge')
            connect(clock_be, getattr(ports, clock))
            if hasattr(fsm_instance._instantiation, '_reset_backedge'):
                reset_be = getattr(fsm_instance._instantiation,
                                   '_reset_backedge')
                connect(reset_be, getattr(ports, reset))

            # Connect outputs
            for (port_name, _) in fsm_mod._pycde_mod.output_ports:
                setattr(ports, port_name, getattr(fsm_instance, port_name))

    # Inherit in and output ports. We do this outside of the wrapped class
    # since we cannot do setattr inside the class scope (def-use).
    for (name, type) in fsm_mod._pycde_mod.input_ports:
        setattr(fsm_hw_mod, name, Input(type))
    for (name, type) in fsm_mod._pycde_mod.output_ports:
        setattr(fsm_hw_mod, name, Output(type))

    # Add clock and additional reset port.
    setattr(fsm_hw_mod, clock, Input(types.i1))

    if reset is not None:
        setattr(fsm_hw_mod, reset, Input(types.i1))

    # The wrapper class now overloads the name of the user-defined FSM.
    # From this point on, instantiating the user FSM class will actually
    # instantiate the wrapper HW module class.
    fsm_hw_mod.__qualname__ = fsm_name
    fsm_hw_mod.__name__ = fsm_name
    fsm_hw_mod.__module__ = fsm_name
    return fsm_hw_mod
Exemplo n.º 23
0
class PolynomialSystem:
    y = Output(types.i32)

    @generator
    def construct(ports):
        i32 = types.i32
        x = hw.ConstantOp(i32, 23)
        poly = PolynomialCompute(Coefficients([62, 42, 6]))("example")
        connect(poly.x, x)
        PolynomialCompute(coefficients=Coefficients([62, 42, 6]))("example2",
                                                                  x=poly.y)
        PolynomialCompute(Coefficients([1, 2, 3, 4, 5]))("example2", x=poly.y)

        cp = CoolPolynomialCompute([4, 42])
        cp.x.connect(23)

        m = ExternWithParams(8, 3)()
        m.name = "pexternInst"

        ports.y = poly.y
Exemplo n.º 24
0
    class PolynomialCompute:
        """Module to compute ax^3 + bx^2 + cx + d for design-time coefficients"""

        # Evaluate polynomial for 'x'.
        x = Input(types.i32)
        y = Output(types.int(8 * 4))

        def __init__(self, name: str):
            """coefficients is in 'd' -> 'a' order."""
            self.instance_name = name

        @staticmethod
        def get_module_name():
            return "PolyComputeForCoeff_" + '_'.join(
                [str(x) for x in coefficients.coeff])

        @generator
        def construct(mod):
            """Implement this module for input 'x'."""

            x = mod.x
            taps = list()
            for power, coeff in enumerate(coefficients.coeff):
                coeffVal = hw.ConstantOp(types.i32, coeff)
                if power == 0:
                    newPartialSum = coeffVal
                else:
                    partialSum = taps[-1]
                    if power == 1:
                        currPow = x
                    else:
                        x_power = [x for i in range(power)]
                        currPow = comb.MulOp(*x_power)
                    newPartialSum = comb.AddOp(partialSum,
                                               comb.MulOp(coeffVal, currPow))

                taps.append(newPartialSum)

            # Final output
            mod.y = taps[-1]
Exemplo n.º 25
0
    def machine_clocked(to_be_wrapped):
        """
    Wrap a class as an FSM machine.

    An FSM PyCDE module is expected to implement:

    - A set of input ports:
        These can be of any type, and are used to drive the FSM.

        Transitions can be specified either as a tuple of (next_state, condition)
        or as a single `next_state` string (unconditionally taken).
        a `condition` function is a function which takes a single input representing
        the `ports` of a component, similar to the `@generator` decorator used
        elsewhere in PyCDE.
    """
        states = {}
        initial_state = None
        for name, v in attributes_of_type(to_be_wrapped, State).items():
            if name in states:
                raise ValueError("Duplicate state name: {}".format(name))
            v.name = name
            states[name] = v
            if v.initial:
                if initial_state is not None:
                    raise ValueError(
                        f"Multiple initial states specified ({name}, {initial_state})."
                    )
                initial_state = name

        if initial_state is None:
            raise ValueError(
                "No initial state specified, please create a state with `initial=True`."
            )

        for name, v in attributes_of_type(to_be_wrapped, Input).items():
            if v.type.width != 1:
                raise ValueError(
                    f"Input port {name} has width {v.type.width}. For now, FSMs only support i1 inputs."
                )

        # At this point, the 'states' attribute should be considered an immutable,
        # ordered list of states.
        to_be_wrapped.states = states.values()
        to_be_wrapped._initial_state = initial_state

        if len(states) == 0:
            raise ValueError("No States defined")

        # Add an output port for each state.
        for state_name, state in states.items():
            output_for_state = Output(types.i1)
            setattr(to_be_wrapped, 'is_' + state_name, output_for_state)
            state.output = output_for_state

        # Store requested clock and reset names.
        to_be_wrapped.clock_name = clock
        to_be_wrapped.reset_name = reset

        # Set module creation and generation callbacks.
        setattr(to_be_wrapped, 'create_cb', create_fsm_machine_op)
        setattr(to_be_wrapped, 'generator_cb', generate_fsm_machine_op)

        # Create a dummy Generator function to trigger module generation.
        # This function doesn't do anything, since all generation logic is embedded
        # within generate_fsm_machine_op. In the future, we may allow an actual
        # @generator function specified by the user if they want to do something
        # specific.
        setattr(to_be_wrapped, 'dummy_generator_f', generator(lambda x: None))

        # Treat the remainder of the class as a module.
        # Rename the fsm_mod before creating the module to ensure that the wrapped
        # module will be named as the user specified (that of fsm_mod), and the
        # FSM itself will have a suffixed name.
        fsm_name = to_be_wrapped.__name__
        to_be_wrapped.__name__ = to_be_wrapped.__name__ + '_impl'
        to_be_wrapped.__qualname__ = to_be_wrapped.__qualname__ + '_impl'
        fsm_mod = module(to_be_wrapped)

        # Next we build the outer wrapper class that contains clock and reset ports.
        fsm_hw_mod = fsm_wrapper_class(fsm_mod=fsm_mod,
                                       fsm_name=fsm_name,
                                       clock=clock,
                                       reset=reset)

        return module(fsm_hw_mod)
Exemplo n.º 26
0
class Taps:
    taps = Output(dim(8, 3))

    @generator
    def build(ports):
        ports.taps = [203, 100, 23]
Exemplo n.º 27
0
class CoolPolynomialCompute:
    x = Input(types.i32)
    y = Output(types.i32)

    def __init__(self, coefficients):
        self.coefficients = coefficients