class Combinational(m.Circuit): name = "Combinational" IO = ["x", m.In(m.UInt(16)), "y", m.In(m.UInt(16)), "z", m.Out(m.UInt(16))] @classmethod def definition(io): m.wire(io.x + io.y, io.z)
def test_two_ops(): Top = m.DefineCircuit("test_two_ops", "I0", m.In(m.UInt(8)), "I1", m.In(m.UInt(8)), "O", m.Out(m.UInt(8))) result = Top.I0 + Top.I1 - Top.I0 m.wire(result, Top.O) m.EndCircuit() m.compile("test_two_ops", Top, output="coreir-verilog", inline=True) # assert check_files_equal(__file__, "build/test_two_ops.v", # "gold/test_two_ops.v") # Roundabout way to do this since we can't pass the --inline flag through # fault's tester interface yet tester = fault.Tester(Top) for i in range(0, 16): I0 = fault.random.random_bv(8) I1 = fault.random.random_bv(8) tester.poke(Top.I0, I0) tester.poke(Top.I1, I1) tester.eval() tester.expect(Top.O, I0 + I1 - I0) tester.compile_and_run(target="verilator", skip_compile=True, directory=".")
class _DDS(m.Circuit): name = 'DDS{}'.format(n) IO = ['I', m.In(m.UInt(n)), "O", m.Out(m.UInt(n))] + m.ClockInterface() @classmethod def definition(io): reg = Register(n) m.wire(reg(m.uint(reg.O) + io.I), io.O)
def test_simple_top(): Top = m.DefineCircuit("Top", "I0", m.In(m.UInt(8)), "I1", m.In(m.UInt(8)), "O", m.Out(m.UInt(8))) sum_ = Top.I0 + Top.I1 m.wire(sum_, Top.O) m.EndCircuit() m.compile("test_simple_top", Top, output="coreir-verilog", inline=True)
class Add210(m.Circuit): IO = ["I",m.In(m.UInt(8)),"O",m.Out(m.UInt(8))]+m.ClockInterface() @classmethod def definition(io): rm = RigelMod() m.wire(io.I,rm.process_input) out = rm.process_output+m.uint(10,8) m.wire(io.O,out) m.wire(rm.CE,m.bit(True))
def execute_alu(a: m.UInt(16), b: m.UInt(16), config: m.Bits(2)) -> (m.UInt(16),): if config == m.bits(0, 2): c = a + b elif config == m.bits(1, 2): c = a - b elif config == m.bits(2, 2): c = a * b else: c = m.bits(0, 16) return (c,)
class MagicPacketTracker(m.Circuit): name = "MagicPacketTracker" IO = [ "push", m.In(m.Bit), "pop", m.In(m.Bit), "captured", m.In(m.Bit), "cnt", m.Out(m.UInt(CNTWID)), "next_cnt", m.Out(m.UInt(CNTWID)), "rst", m.In(m.Reset) ] + m.ClockInterface() @classmethod def definition(io): cntreg = DefineRegister(CNTWID, init=0, has_ce=False, has_reset=True, _type=m.UInt) pop_cnt = cntreg(name="pop_cnt") # wire clock m.wireclock(io, pop_cnt) # wire reset m.wire(pop_cnt.RESET, io.rst) # increment enable logic incr_mask = m.bit((pop_cnt.O < m.uint(DEPTH, CNTWID)) & (io.push) & (~io.captured)) wide_incr_mask = repeat(incr_mask, CNTWID) # intermediate signal push_cnt = m.uint(pop_cnt.O + m.uint(m.uint(1, CNTWID) & wide_incr_mask)) # decrement enable logic decr_mask = m.bit((push_cnt > m.uint(0, CNTWID)) & (io.pop)) wide_decr_mask = repeat(decr_mask, CNTWID) # wire next state cnt_update = push_cnt - m.uint(m.uint(1, CNTWID) & wide_decr_mask) m.wire(pop_cnt.I, cnt_update) # wire output m.wire(pop_cnt.O, io.cnt) m.wire(cnt_update, io.next_cnt)
def DefineAdder(N): T = m.UInt(N) class Adder(m.Circuit): name = "Adder{}".format(N) IO = [ "a", m.In(T), "b", m.In(T), "cin", m.In(m.Bit), "out", m.Out(T), "cout", m.Out(m.Bit) ] @classmethod def definition(io): adders = [FullAdder() for _ in range(N)] circ = m.braid(adders, foldargs={"cin": "cout"}) m.wire(io.a, circ.a) m.wire(io.b, circ.b) m.wire(io.cin, circ.cin) m.wire(io.cout, circ.cout) m.wire(io.out, circ.out) return Adder
def create_clb(WireWidth): W = m.UInt(WireWidth) class CLB(m.Circuit): name = 'configurable_logic_block' IO = [ "operand0", m.In(m.Bits(WireWidth)), "operand1", m.In(m.Bits(WireWidth)), "result", m.Out(m.Bits(WireWidth)), "clk", m.In(m.Clock), "rst", m.In(m.Reset), "config_data", m.In(m.Bits(32)), "config_en", m.In(m.Bit) ] @classmethod def definition(io): # Configuration data config_reg = mantle.Register(32, init=0, has_ce=True, has_reset=True) m.wire(io.config_data, config_reg.I) m.wire(io.clk, config_reg.CLK) m.wire(io.config_en, config_reg.CE) rst_inv = mantle.Invert(1) m.wire(rst_inv.I[0], io.rst) m.wire(rst_inv.O[0], config_reg.RESET) # Operations in CLB and_op = mantle.And(2, 1) m.wire(io.operand0, and_op.I0) m.wire(io.operand1, and_op.I1) or_op = mantle.Or(2, 1) m.wire(io.operand0, or_op.I0) m.wire(io.operand1, or_op.I1) xor_op = mantle.XOr(2, 1) m.wire(io.operand0, xor_op.I0) m.wire(io.operand1, xor_op.I1) not_op = mantle.Invert(1) m.wire(io.operand0, not_op.I) # Config mux config_mux = mantle.Mux(height=4, width=1) m.wire(config_mux.O, io.result) m.wire(config_mux.S, config_reg.O[0:2]) m.wire(and_op.O, config_mux.I0) m.wire(or_op.O, config_mux.I1) m.wire(xor_op.O, config_mux.I2) m.wire(not_op.O, config_mux.I3) return CLB
class SimpleALU(m.Circuit): IO = ["a", m.In(m.UInt(16)), "b", m.In(m.UInt(16)), "c", m.Out(m.UInt(16)), "config", m.In(m.Bits(2))] @m.circuit.combinational def execute_alu(a: m.UInt(16), b: m.UInt(16), config: m.Bits(2)) -> (m.UInt(16),): if config == m.bits(0, 2): c = a + b elif config == m.bits(1, 2): c = a - b elif config == m.bits(2, 2): c = a * b else: c = m.bits(0, 16) return (c,) @classmethod def definition(io): io.c <= io.execute_alu(io.a, io.b, io.config)
def create_connect_box(WireWidth): W = m.UInt(WireWidth) class ConnectBox(m.Circuit): name = 'connect_box' IO = [ "clk", m.In(m.Clock), "rst", m.In(m.Reset), "config_data", m.In(m.Bits(32)), "config_en", m.In(m.Bit), "track_0_in", m.In(m.Bits(WireWidth)), "track_1_in", m.In(m.Bits(WireWidth)), "track_2_in", m.In(m.Bits(WireWidth)), "track_3_in", m.In(m.Bits(WireWidth)), "track_0_out", m.In(m.Bits(WireWidth)), "track_1_out", m.In(m.Bits(WireWidth)), "track_2_out", m.In(m.Bits(WireWidth)), "track_3_out", m.In(m.Bits(WireWidth)), "out", m.Out(m.Bits(WireWidth)) ] @classmethod def definition(io): # Configuration data config_reg = mantle.Register(32, init=0, has_ce=True, has_reset=True) m.wire(io.config_data, config_reg.I) m.wire(io.clk, config_reg.CLK) m.wire(io.config_en, config_reg.CE) rst_inv = mantle.Invert(1) m.wire(rst_inv.I[0], io.rst) m.wire(rst_inv.O[0], config_reg.RESET) # Config mux config_mux = mantle.Mux(height=8, width=1) m.wire(config_mux.O, io.out) m.wire(config_mux.S, config_reg.O[0:3]) m.wire(io.track_0_in, config_mux.I0) m.wire(io.track_1_in, config_mux.I1) m.wire(io.track_2_in, config_mux.I2) m.wire(io.track_3_in, config_mux.I3) m.wire(io.track_0_out, config_mux.I4) m.wire(io.track_1_out, config_mux.I5) m.wire(io.track_2_out, config_mux.I6) m.wire(io.track_3_out, config_mux.I7) return ConnectBox
def fsm_logic(current_state: m.Bits(2), pixel_count: m.UInt(11), frameValid: m.Bit, real_href: m.Bit) -> (m.Bits(2), m.UInt(11)): """Returns next_state, next_pixel_count""" if current_state == IDLE: next_state = HBLANK if frameValid else IDLE next_pixel_count = m.bits(0, 11) elif current_state == HBLANK: if real_href: next_state = HACT next_pixel_count = PIX_PER_LINE else: next_state = HBLANK next_pixel_count = m.bits(0, 11) elif current_state == HACT: next_state = HBLANK if pixel_count == m.bits(1, 11) else HACT # TODO: Support AugAssign node # next_pixel_count -= 1 next_pixel_count = pixel_count - m.uint(1, 11) else: next_state = IDLE next_pixel_count = m.bits(0, 11) return next_state, next_pixel_count
def test_const(): Top = m.DefineCircuit("test_const", "I0", m.In(m.UInt(8)), "I1", m.In(m.UInt(8)), "O", m.Out(m.UInt(8))) result = Top.I0 + Top.I1 * m.uint(3, 8) m.wire(result, Top.O) m.EndCircuit() m.compile("test_const", Top, output="coreir-verilog", inline=True) tester = fault.Tester(Top) for i in range(0, 16): I0 = fault.random.random_bv(8) I1 = fault.random.random_bv(8) tester.poke(Top.I0, I0) tester.poke(Top.I1, I1) tester.eval() tester.expect(Top.O, I0 + I1 * 3) tester.compile_and_run(target="verilator", skip_compile=True, directory=".")
def DefineAdder(N): T = m.UInt(N) class Adder(m.Circuit): name = "Adder{}".format(N) IO = ["I0", m.In(T), "I1", m.In(T), "CIN", m.In(m.Bit), "O", m.Out(T), "COUT", m.Out(m.Bit)] @classmethod def definition(io): adders = [mantle.FullAdder() for _ in range(N)] adders = m.fold(adders, foldargs={"CIN":"COUT"}) COUT, O = adders(I0=io.I0, I1=io.I1, CIN=io.CIN) m.wire(O, io.O) m.wire(COUT, io.COUT) return Adder
class SimpleALU(m.Circuit): IO = [ "a", m.In(m.UInt(16)), "b", m.In(m.UInt(16)), "c", m.Out(m.UInt(16)), "config_data", m.In(m.Bits(2)), "config_en", m.In(m.Enable), ] + m.ClockInterface() @classmethod def definition(io): opcode = ConfigReg(name="config_reg")(io.config_data, CE=io.config_en) io.c <= mantle.mux( # udiv not implemented # [io.a + io.b, io.a - io.b, io.a * io.b, io.a / io.b], opcode) # use arbitrary fourth op [io.a + io.b, io.a - io.b, io.a * io.b, io.b - io.a], opcode)
class SimpleALU(m.Circuit): name = "SimpleALU" IO = [ "a", m.In(m.UInt(4)), "b", m.In(m.UInt(4)), "opcode", m.In(m.UInt(2)), "out", m.Out(m.UInt(4)) ] @classmethod def definition(io): is_op0 = io.opcode == m.uint(0, n=2) is_op1 = io.opcode == m.uint(1, n=2) is_op2 = io.opcode == m.uint(2, n=2) is_op3 = io.opcode == m.uint(3, n=2) op0_out = io.a + io.b op1_out = io.a - io.b op2_out = io.a op3_out = io.b m.wire( io.out, one_hot_mux([is_op0, is_op1, is_op2, is_op3], [op0_out, op1_out, op2_out, op3_out]))
def DefineCharacterMatcher(c: str): assert (len(c) == 1) CharType = magma.UInt(8) class _CharacterMatcher(magma.Circuit): name = "CharacterMatcher_" + c IO = ["char", magma.In(CharType), "match", magma.Out(magma.Bit)] @classmethod def definition(io): comp = mantle.EQ(8) magma.wire(magma.uint(ord(c[0]), 8), comp.I0) magma.wire(io.char, comp.I1) magma.wire(comp.O, io.match) return _CharacterMatcher
def DefineMatcher(regex: regex.Expr): CharType = magma.UInt(8) class _Matcher(magma.Circuit): name = "Matcher" IO = ["char", magma.In(CharType), "match", magma.Out(magma.Bit)] + magma.ClockInterface() @classmethod def definition(io): (i, o) = regex.to_circuit(io.char) magma.wire(1, i) magma.wire(o, io.match) return _Matcher
def Color(num_bits): return m.UInt(num_bits)
def create_top(N): grid_len = m.UInt(N) class Top(m.Circuit): name = "top" IO = [ "clk", m.In(m.Clock), "reset", m.In(m.Reset), "config_addr", m.In(m.Bits(32)), "config_data", m.In(m.Bits(32)) ] for i in range(0, N): IO.append("in_wire_" + str(i)) IO.append(m.In(m.Bit)) IO.append("out_wire_" + str(i)) IO.append(m.Out(m.Bit)) @classmethod def definition(io): tile_map = {} io_num = 0 for i in range(0, N): io_tile = create_io1in_pad()() tile_map['io_in_pad_' + str(io_num)] = io_tile m.wire(io_tile.clk, io.clk) m.wire(io_tile.rst, io.reset) m.wire(io_tile.top_pin[0], getattr(io, 'in_wire_' + str(i))) io_num += 1 tile_id = 1 for i in range(0, N): out_tile = create_io1out_pad()() tile_map['io_out_pad_' + str(tile_id)] = out_tile m.wire(out_tile.clk, io.clk) m.wire(out_tile.rst, io.reset) m.wire(out_tile.tile_id, m.uint(tile_id, 16)) m.wire(out_tile.config_addr, io.config_addr) m.wire(out_tile.config_data, io.config_data) m.wire(out_tile.top_pin[0], getattr(io, 'out_wire_' + str(i))) tile_id += 1 for row in range(0, N): for col in range(0, N): op_tile = create_pe_tile()() tile_map['pe_tile_' + str(row) + '_' + str(col)] = op_tile m.wire(op_tile.clk, io.clk) m.wire(op_tile.rst, io.reset) m.wire(op_tile.tile_id, m.uint(tile_id, 16)) m.wire(op_tile.config_addr, io.config_addr) m.wire(op_tile.config_data, io.config_data) tile_id += 1 # Connect top ios to first pe rows print('Tile names') for tile in tile_map: print('\t' + tile) print('# of tiles = ', str(tile_id - 1)) # IO ins to top for col in range(0, N): in_pad = tile_map['io_in_pad_' + str(col)] top_pe = tile_map['pe_tile_0_' + str(col)] for track in range(0, 4): m.wire( getattr(in_pad, 'pin_' + str(track)), getattr(top_pe, 'side_3_track_' + str(track) + '_in')[0]) # bottom to IO outs for col in range(1, N + 1): out_pad = tile_map['io_out_pad_' + str(col)] top_pe = tile_map['pe_tile_' + str(N - 1) + '_' + str(col - 1)] for track in range(0, 4): m.wire( getattr(out_pad, 'pin_' + str(track)), getattr(top_pe, 'side_1_track_' + str(track) + '_out')[0]) # Wire up every side bottom to every side top for row in range(0, N - 1): for col in range(0, N): top_pe = tile_map['pe_tile_' + str(row) + '_' + str(col)] bot_pe = tile_map['pe_tile_' + str(row + 1) + '_' + str(col)] for track in range(0, 4): m.wire( getattr(bot_pe, 'side_3_track_' + str(track) + '_in'), getattr(top_pe, 'side_1_track_' + str(track) + '_out')) m.wire( getattr(bot_pe, 'side_3_track_' + str(track) + '_out'), getattr(top_pe, 'side_1_track_' + str(track) + '_in')) for row in range(0, N): for col in range(0, N - 1): lef_pe = tile_map['pe_tile_' + str(row) + '_' + str(col)] rit_pe = tile_map['pe_tile_' + str(row) + '_' + str(col + 1)] for track in range(0, 4): m.wire( getattr(rit_pe, 'side_2_track_' + str(track) + '_in'), getattr(lef_pe, 'side_0_track_' + str(track) + '_out')) m.wire( getattr(rit_pe, 'side_2_track_' + str(track) + '_out'), getattr(lef_pe, 'side_0_track_' + str(track) + '_in')) # Wire side 0 of far right to constants for row in range(0, N): col = N - 1 rit_pe = tile_map['pe_tile_' + str(row) + '_' + str(col)] for track in range(0, 4): m.wire( m.uint(1, 0), getattr(rit_pe, 'side_0_track_' + str(track) + '_in')) # Wire side 1 of bottom to constants for col in range(0, N): row = N - 1 rit_pe = tile_map['pe_tile_' + str(row) + '_' + str(col)] for track in range(0, 4): m.wire( m.uint(1, 0), getattr(rit_pe, 'side_1_track_' + str(track) + '_in')) for row in range(0, N): lef_pe = tile_map['pe_tile_' + str(row) + '_' + str(0)] for track in range(0, 4): m.wire( getattr(lef_pe, 'side_2_track_' + str(track) + '_in'), m.uint(1, 1)) rit_pe = tile_map['pe_tile_' + str(row) + '_' + str(N - 1)] for track in range(0, 4): m.wire( getattr(rit_pe, 'side_0_track_' + str(track) + '_in'), m.uint(1, 1)) return Top
import magma as m m.set_mantle_target("coreir") import mantle # This notebook demonstrates using native Python functions to construct circuits. # In[2]: def clb(a, b, c, d): return (a & b) | (~c & d) T = m.UInt(16) class Combinational(m.Circuit): name = "Combinational" IO = ["a", m.In(T), "b", m.In(T), "c", m.Out(T)] @classmethod def definition(io): m.wire(clb(io.a, io.b, io.a, io.b), io.c) # In[3]: from magma.simulator import PythonSimulator from magma.bit_vector import BitVector simulator = PythonSimulator(Combinational)
class NastiDataIO(m.Product): data = m.UInt(nasti_params.x_data_bits) last = m.Bit
class Scoreboard(m.Circuit): NUM_REQS = 4 name = "Scoreboard" assert not ARBITER or NUM_REQS is not None, "If using arbiter, need to supply NUM_REQS" assert not ARBITER or QWID is not None, "If using arbiter, need to supply QWID" if ARBITER: IO = [ "push", m.In(m.Bits(NUM_REQS)), "start", m.In(m.Bit), "rst", m.In(m.Reset), # include blk sometime in the future "data_in", m.In(m.Array(N=NUM_REQS, T=m.Bits(DATAWID))), "input_quantums", m.In(m.Array(N=NUM_REQS, T=m.UInt(QWID))), "data_out_vld", m.Out(m.Bit) ] + m.ClockInterface() else: IO = [ "push", m.In(m.Bit), "pop", m.In(m.Bit), "start", m.In(m.Bit), "rst", m.In(m.Reset), "data_in", m.In(m.Bits(DATAWID)), "data_out_vld", m.Out(m.Bit) ] + m.ClockInterface() if NUM_REQS is None: NUM_REQS = 1 @classmethod def definition(io): en = DefineRegister(1, init=0, has_ce=False, has_reset=True, _type=m.Bits)(name="en") # wire clock m.wireclock(en, io) # wire reset m.wire(io.rst, en.RESET) # enable only goes high once, then stays high m.wire(en.O | m.bits(io.start), en.I) mpt = DefineMagicPacketTracker(DEPTH)() # wire up magic packet tracker m.wire(en.O, m.bits(mpt.captured)) m.wire(io.rst, mpt.rst) m.wireclock(io, mpt) if not ARBITER: m.wire(io.push, mpt.push) m.wire(io.pop, mpt.pop) fifo = DefineFIFO(DATAWID, DEPTH)() # wire up fifo m.wire(io.push, fifo.push) m.wire(io.pop, fifo.pop) m.wire(io.rst, fifo.rst) m.wire(io.data_in, fifo.data_in) m.wireclock(io, fifo) else: m.wire(io.push[0], mpt.push) fifos = list() for i in range(NUM_REQS): f = DefineFIFO(DATAWID, DEPTH)(name="fifo_{}".format(i)) fifos.append(f) m.wire(io.push[i], f.push) m.wire(io.rst, f.rst) m.wire(io.data_in[i], f.data_in) m.wireclock(io, f) # Need to wire things up if ARBITER: arb = DefineDWRR(NUM_REQS, QWID, DATAWID)(name="arb") m.wire(io.rst, arb.rst) m.wire(io.input_quantums, arb.quantums) for i in range(NUM_REQS): m.wire(~fifos[i].empty, arb.reqs[i]) m.wire(arb.gnt[i], fifos[i].pop) m.wire(arb.gnt[0], mpt.pop) # vld out # TODO handle missing magic packet -- need to reset everything. Or keep as an assumption/restriction m.wire( m.bit(en.O) & eq(m.uint(mpt.next_cnt), m.uint(0, (DEPTH - 1).bit_length())) & eq(m.uint(mpt.cnt), m.uint(1, (DEPTH - 1).bit_length())), io.data_out_vld)
def rigelTypeToMagmaType(ty): assert(types.isType(ty)) if ty.isUint(ty): return m.UInt(ty.verilogBits(ty)) else: assert(false)
class FIFO(m.Circuit): name = "FIFO" IO = [ "push", m.In(m.Bit), "pop", m.In(m.Bit), "rst", m.In(m.Reset), "data_in", m.In(m.UInt(WIDTH)), "data_out", m.Out(m.UInt(WIDTH)), "empty", m.Out(m.Bit), "full", m.Out(m.Bit) ] + m.ClockInterface() @classmethod def definition(io): # didn't work with coreir because the rst/bit conversion # clkEn = io.push | io.pop | m.bit(io.rst) ########################## pointer logic ############################## ptrreg = DefineRegister(PTRWID, init=0, has_ce=True, has_reset=True, _type=m.UInt) wrPtr = ptrreg(name="wrPtr") rdPtr = ptrreg(name="rdPtr") # wire clocks m.wireclock(io, wrPtr) m.wireclock(io, rdPtr) # wire resets m.wire(wrPtr.RESET, io.rst) m.wire(rdPtr.RESET, io.rst) # wire enables m.wire(wrPtr.CE, io.push) m.wire(rdPtr.CE, io.pop) # next values increment by one m.wire(wrPtr.I, wrPtr.O + m.uint(1, PTRWID)) m.wire(rdPtr.I, rdPtr.O + m.uint(1, PTRWID)) ######################### end pointer logic ########################### ######################### full and empty logic ######################## m.wire(io.empty, wrPtr.O == rdPtr.O) m.wire(io.full, (wrPtr.O[1:PTRWID] == rdPtr.O[1:PTRWID]) & (wrPtr.O[0] != rdPtr.O[0])) ######################### end full and empty logic #################### ########################### entry logic ############################### # Create and write entries = [] entryReg = DefineRegister(WIDTH, init=0, has_ce=True, has_reset=False, _type=m.Bits) for i in range(DEPTH): entry = entryReg(name="entry" + str(i)) m.wire( entry.CE, io.push & m.bit(m.uint(wrPtr.O[1:PTRWID]) == m.uint(i, PTRWID - 1))) m.wire(entry.I, io.data_in) entries.append(entry) # Connect mux outmux = Mux(DEPTH, WIDTH) for i in range(DEPTH): m.wire(getattr(outmux, "I" + str(i)), entries[i].O) m.wire(rdPtr.O[1:PTRWID], outmux.S) m.wire(outmux.O, io.data_out)