def test_instance_name_tile(): core = DummyCore() circuit = core.circuit() assert str( circuit.instances ) == '[MuxWithDefaultWrapper_2_32_8_0_inst0 = MuxWithDefaultWrapper_2_32_8_0(), dummy_1 = ConfigRegister_32_8_32_0(name="dummy_1"), dummy_2 = ConfigRegister_32_8_32_1(name="dummy_2")]' # noqa with tempfile.TemporaryDirectory() as tempdir: m.compile(f"{tempdir}/core", circuit) assert check_files_equal(f"{tempdir}/core.v", "tests/generator/gold/core_instance_name.v")
def test_instance_name_tile(): core = DummyCore() circuit = core.circuit() print(str(circuit.instances)) assert str( circuit.instances ) == '[config_reg_0 = ConfigRegister_32_8_32_0(name="config_reg_0"), dummy_1_inst0 = dummy_1(), config_reg_1 = ConfigRegister_32_8_32_1(name="config_reg_1"), dummy_2_inst0 = dummy_2(), MuxWrapper_2_32_inst0 = MuxWrapper_2_32()]' # noqa with tempfile.TemporaryDirectory() as tempdir: m.compile(f"{tempdir}/core", circuit, output="coreir-verilog") assert check_files_equal(f"{tempdir}/core.v", "tests/generator/gold/core_instance_name.v")
def test_dump_pnr(): num_tracks = 2 addr_width = 8 data_width = 32 bit_widths = [1, 16] tile_id_width = 16 chip_size = 2 track_length = 1 # creates all the cores here # we don't want duplicated cores when snapping into different interconnect # graphs cores = {} for x in range(chip_size): for y in range(chip_size): cores[(x, y)] = DummyCore() def create_core(xx: int, yy: int): return cores[(xx, yy)] in_conn = [] out_conn = [] for side in SwitchBoxSide: in_conn.append((side, SwitchBoxIO.SB_IN)) out_conn.append((side, SwitchBoxIO.SB_OUT)) pipeline_regs = [] for track in range(num_tracks): for side in SwitchBoxSide: pipeline_regs.append((track, side)) ics = {} for bit_width in bit_widths: ic = create_uniform_interconnect(chip_size, chip_size, bit_width, create_core, { f"data_in_{bit_width}b": in_conn, f"data_out_{bit_width}b": out_conn }, {track_length: num_tracks}, SwitchBoxType.Disjoint, pipeline_reg=pipeline_regs) ics[bit_width] = ic interconnect = Interconnect(ics, addr_width, data_width, tile_id_width, lift_ports=True) design_name = "test" with tempfile.TemporaryDirectory() as tempdir: interconnect.dump_pnr(tempdir, design_name) assert os.path.isfile(os.path.join(tempdir, f"{design_name}.info")) assert os.path.isfile(os.path.join(tempdir, "1.graph")) assert os.path.isfile(os.path.join(tempdir, "16.graph")) assert os.path.isfile(os.path.join(tempdir, f"{design_name}.layout"))
def interconnect_route(): chip_size = 2 # creates all the cores here # we don't want duplicated cores when snapping into different interconnect # graphs cores = {} for x in range(0, chip_size + 2): for y in range(0, chip_size + 2): cores[(x, y)] = IO16bit() for x in range(1, 1 + chip_size): for y in range(1, 1 + chip_size): cores[(x, y)] = DummyCore() # corners for x, y in [(0, 0), (0, chip_size + 1), (chip_size + 1, 0), (chip_size + 1, chip_size + 1)]: cores[(x, y)] = None interconnect = create_cgra(chip_size, True, cores_input=cores) netlist = { "e0": [("I0", "io2f_16"), ("r0", "reg")], "e1": [("r0", "reg"), ("D0", "data_in_16b")], "e2": [("D0", "data_out_16b"), ("I1", "f2io_16")] } bus = {"e0": 16, "e1": 16, "e2": 16} with tempfile.TemporaryDirectory() as tempdir: _, route = pnr(interconnect, (netlist, bus), cwd=tempdir) # two paths route_path = [route["e0"][0], route["e1"][0], route["e2"][0]] return interconnect, route_path
def create_dummy_cgra(chip_size, num_tracks, reg_mode, wiring, num_cfg=1): addr_width = 8 data_width = 32 bit_widths = [1, 16] tile_id_width = 16 track_length = 1 # creates all the cores here # we don't want duplicated cores when snapping into different interconnect # graphs cores = {} for x in range(chip_size): for y in range(chip_size): cores[(x, y)] = DummyCore() def create_core(xx: int, yy: int): return cores[(xx, yy)] in_conn = [] out_conn = [] for side in SwitchBoxSide: in_conn.append((side, SwitchBoxIO.SB_IN)) out_conn.append((side, SwitchBoxIO.SB_OUT)) pipeline_regs = [] for track in range(num_tracks): for side in SwitchBoxSide: pipeline_regs.append((track, side)) # if reg mode is off, reset to empty if not reg_mode: pipeline_regs = [] ics = {} for bit_width in bit_widths: ic = create_uniform_interconnect(chip_size, chip_size, bit_width, create_core, { f"data_in_{bit_width}b": in_conn, f"data_out_{bit_width}b": out_conn }, {track_length: num_tracks}, SwitchBoxType.Disjoint, pipeline_regs) ics[bit_width] = ic interconnect = Interconnect(ics, addr_width, data_width, tile_id_width, lift_ports=True) # finalize the design interconnect.finalize() # wiring if wiring == GlobalSignalWiring.Fanout: apply_global_fanout_wiring(interconnect, IOSide.None_) elif wiring == GlobalSignalWiring.Meso: apply_global_meso_wiring(interconnect, IOSide.None_) else: assert wiring == GlobalSignalWiring.ParallelMeso apply_global_parallel_meso_wiring(interconnect, IOSide.None_, num_cfg) return bit_widths, data_width, ics, interconnect
def test_empty_tile_util(): chip_size = 4 margin = 1 sides = IOSide.North | IOSide.East | IOSide.South | IOSide.West bit_widths = [1, 16] cores = {} track_length = 1 num_tracks = 2 addr_width = 8 data_width = 32 tile_id_width = 16 for x in range(chip_size + 2 * margin): for y in range(chip_size + 2 * margin): if x in range(margin) or \ x in range(chip_size - margin, chip_size) or \ y in range(margin) or \ y in range(chip_size - margin, chip_size): cores[(x, y)] = None else: cores[(x, y)] = DummyCore() def core_fn(x, y): return cores[(x, y)] in_conn = [] out_conn = [] for side in SwitchBoxSide: in_conn.append((side, SwitchBoxIO.SB_IN)) out_conn.append((side, SwitchBoxIO.SB_OUT)) ics = {} for bit_width in bit_widths: ic = create_uniform_interconnect(chip_size, chip_size, bit_width, core_fn, { f"data_in_{bit_width}b": in_conn, f"data_out_{bit_width}b": out_conn }, {track_length: num_tracks}, SwitchBoxType.Disjoint, io_sides=sides) ics[bit_width] = ic interconnect = Interconnect(ics, addr_width, data_width, tile_id_width) interconnect.finalize() # wiring apply_global_meso_wiring(interconnect) circuit = interconnect.circuit() with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "interconnect") magma.compile(filename, circuit, output="coreir-verilog")
def test_empty_switch_box(): bit_width = 1 core = CoreInterface(DummyCore()) tile = Tile(0, 0, bit_width, SwitchBox(0, 0, 0, bit_width, [])) tile.set_core(core) # because we need something to be connected to the core node # otherwise it's an illogical tile sb_node = SwitchBoxNode(0, 1, 0, bit_width, SwitchBoxSide.NORTH, SwitchBoxIO.SB_IN) sb_node.add_edge(tile.ports["data_in_1b"]) tile_circuit = TileCircuit({bit_width: tile}, 8, 32) tile_circuit.finalize() # also need to ground the 16 bit tile_circuit.wire(Const(0), tile_circuit.core.ports["data_in_16b"]) circuit = tile_circuit.circuit() with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "tile") magma.compile(filename, circuit, output="coreir-verilog")
def test_1x1(double_buffer): ics = {} in_conn = [] out_conn = [] addr_width = 8 data_width = 32 for side in SwitchBoxSide: in_conn.append((side, SwitchBoxIO.SB_IN)) out_conn.append((side, SwitchBoxIO.SB_OUT)) core = DummyCore() for bit_width in {1, 16}: ic = create_uniform_interconnect(1, 1, bit_width, lambda _, __: core, {f"data_in_{bit_width}b": in_conn, f"data_out_{bit_width}b": out_conn}, {1: 2}, SwitchBoxType.Disjoint) ics[bit_width] = ic interconnect = Interconnect(ics, addr_width, data_width, 16, lift_ports=True, double_buffer=double_buffer) interconnect.finalize() apply_global_fanout_wiring(interconnect) circuit = interconnect.circuit() with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "test1x1") magma.compile(filename, circuit, output="coreir-verilog") # test routing for 1x1 compares = {} for seed in {0, 1}: routing_result, _ = route_one_tile(interconnect, 0, 0, ports=["data_in_16b", "data_out_16b"], seed=seed) # routing result ordering is the same as ports assert len(routing_result) == 2 bs = interconnect.get_route_bitstream(routing_result) assert len(bs) > 0 compares[seed] = bs for i in range(2): assert compares[0][i] != compares[1][i]
def test_double_buffer(): addr_width = 8 data_width = 32 bit_widths = [1, 16] num_tracks = 5 tile_id_width = 16 x = 0 y = 0 dummy_core = DummyCore() core = CoreInterface(dummy_core) tiles: Dict[int, Tile] = {} for bit_width in bit_widths: # we use disjoint switch here switchbox = DisjointSwitchBox(x, y, num_tracks, bit_width) tile = Tile(x, y, bit_width, switchbox) tiles[bit_width] = tile # set the core and core connection # here all the input ports are connect to SB_IN and all output ports are # connected to SB_OUT input_connections = [] for track in range(num_tracks): for side in SwitchBoxSide: input_connections.append( SBConnectionType(side, track, SwitchBoxIO.SB_IN)) output_connections = [] for track in range(num_tracks): for side in SwitchBoxSide: output_connections.append( SBConnectionType(side, track, SwitchBoxIO.SB_OUT)) for bit_width, tile in tiles.items(): tile.set_core(core) input_port_name = f"data_in_{bit_width}b" output_port_name = f"data_out_{bit_width}b" tile.set_core_connection(input_port_name, input_connections) tile.set_core_connection(output_port_name, output_connections) tile_circuit = TileCircuit(tiles, addr_width, data_width, tile_id_width=tile_id_width, double_buffer=True) tile_circuit.finalize() circuit = tile_circuit.circuit() bit_width = 16 # find corresponding sb sb_circuit: SB = None for _, sb in tile_circuit.sbs.items(): if sb.switchbox.width == bit_width: sb_circuit = sb break assert sb_circuit is not None # find that connection box input_port_name = f"data_in_{bit_width}b" cb_circuit: CB = None for _, cb in tile_circuit.cbs.items(): if cb.node.name == input_port_name: cb_circuit = cb break assert cb_circuit output_port_name = f"data_out_{bit_width}b" out_port_node = tile_circuit.tiles[bit_width].ports[output_port_name] in_port_node = tile_circuit.tiles[bit_width].ports[input_port_name] input_1 = sb_circuit.switchbox.get_sb(SwitchBoxSide.NORTH, 0, SwitchBoxIO.SB_IN) input_1_name = create_name(str(input_1)) input_2 = sb_circuit.switchbox.get_sb(SwitchBoxSide.EAST, 1, SwitchBoxIO.SB_IN) input_2_name = create_name(str(input_2)) output_sb = sb_circuit.switchbox.get_sb(SwitchBoxSide.SOUTH, 2, SwitchBoxIO.SB_OUT) output_name = create_name(str(output_sb)) input_1_bitstream = tile_circuit.get_route_bitstream_config( input_1, in_port_node) input_2_bitstream = tile_circuit.get_route_bitstream_config( input_2, in_port_node) output_bitstream = tile_circuit.get_route_bitstream_config( out_port_node, output_sb) # notice that both of them will be configured using the double buffer scheme def get_config_data(config_data, reg_data): for reg_addr, feat_addr, config_value in reg_data: reg_addr = reg_addr << tile_circuit.feature_config_slice.start feat_addr = feat_addr << tile_circuit.tile_id_width addr = reg_addr | feat_addr addr = BitVector[data_width](addr) | BitVector[data_width](0) config_data.append((addr, config_value)) input1_config_data = [] input2_config_data = [] get_config_data(input1_config_data, [input_1_bitstream, output_bitstream]) get_config_data(input2_config_data, [input_2_bitstream, output_bitstream]) input1_config_data = compress_config_data(input1_config_data) input2_config_data = compress_config_data(input2_config_data) tester = BasicTester(circuit, circuit.clk, circuit.reset) tester.poke(circuit.tile_id, 0) for addr, config_value in input1_config_data: tester.configure(addr, config_value) tester.config_read(addr) tester.eval() tester.expect(circuit.read_config_data, config_value) # configure the double buffer register tester.poke(circuit.config_db, 1) for addr, config_value in input2_config_data: tester.configure(addr, config_value) tester.config_read(addr) tester.eval() tester.expect(circuit.read_config_data, config_value) # the route should still be input 1 port = circuit.interface.ports[input_1_name] tester.poke(port, 42) port = circuit.interface.ports[input_2_name] tester.poke(port, 43) tester.eval() tester.expect(circuit.interface.ports[output_name], 42) # now use the double buffer tester.poke(circuit.use_db, 1) tester.eval() tester.expect(circuit.interface.ports[output_name], 43) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def test_tile(num_tracks: int, add_additional_core: bool): import random random.seed(0) addr_width = 8 data_width = 32 bit_widths = [1, 16] tile_id_width = 16 x = 0 y = 0 dummy_core = DummyCore() core = CoreInterface(dummy_core) if add_additional_core: c = AdditionalDummyCore() additional_core = CoreInterface(c) else: additional_core = None tiles: Dict[int, Tile] = {} for bit_width in bit_widths: # we use disjoint switch here switchbox = DisjointSwitchBox(x, y, num_tracks, bit_width) tile = Tile(x, y, bit_width, switchbox) tiles[bit_width] = tile # set the core and core connection # here all the input ports are connect to SB_IN and all output ports are # connected to SB_OUT input_connections = [] for track in range(num_tracks): for side in SwitchBoxSide: input_connections.append( SBConnectionType(side, track, SwitchBoxIO.SB_IN)) output_connections = [] for track in range(num_tracks): for side in SwitchBoxSide: output_connections.append( SBConnectionType(side, track, SwitchBoxIO.SB_OUT)) for bit_width, tile in tiles.items(): tile.set_core(core) if add_additional_core: connection_type = CoreConnectionType.Core | CoreConnectionType.CB tile.add_additional_core(additional_core, connection_type) input_port_name = f"data_in_{bit_width}b" input_port_name_extra = f"data_in_{bit_width}b_extra" output_port_name = f"data_out_{bit_width}b" tile.set_core_connection(input_port_name, input_connections) tile.set_core_connection(output_port_name, output_connections) if add_additional_core: tile.set_core_connection(input_port_name_extra, input_connections) tile_circuit = TileCircuit(tiles, addr_width, data_width, tile_id_width=tile_id_width) # finalize it tile_circuit.finalize() circuit = tile_circuit.circuit() # set up the configuration and test data # there are several things we are interested in the tile level and # need to test # 1. given an input to SB_IN, and configure it to CB, will the core # receive the data or not # 2. given an output signal from core, and configure it to SB, will the # SB_OUT receive the data or not # However, because we can only poke input ports, we cannot test #2 in the # current environment. As a result, we will combined these 2 together, that # is: # given an SB_IN signal, we configure the CB to the data_in, then configure # the SB_OUT to receive the signal raw_config_data = [] config_data = [] test_data = [] tile_id = fault.random.random_bv(tile_id_width) for bit_width in bit_widths: # find corresponding sb sb_circuit: SB = None for _, sb in tile_circuit.sbs.items(): if sb.switchbox.width == bit_width: sb_circuit = sb break assert sb_circuit is not None # input if add_additional_core: input_port_name = f"data_in_{bit_width}b_extra" else: input_port_name = f"data_in_{bit_width}b" in_port_node = tile_circuit.tiles[bit_width].ports[input_port_name] # find that connection box cb_circuit: CB = None for _, cb in tile_circuit.cbs.items(): if cb.node.name == input_port_name: cb_circuit = cb break assert cb_circuit output_port_name = f"data_out_{bit_width}b" out_port_node = tile_circuit.tiles[bit_width].ports[output_port_name] all_sbs = sb_circuit.switchbox.get_all_sbs() for in_sb_node in all_sbs: if in_sb_node.io != SwitchBoxIO.SB_IN: continue for out_sb_node in all_sbs: if out_sb_node.io != SwitchBoxIO.SB_OUT: continue # find the output node's index to that switch box node data0 = tile_circuit.get_route_bitstream_config( in_sb_node, in_port_node) data1 = tile_circuit.get_route_bitstream_config( out_port_node, out_sb_node) raw_config_data.append(data0) raw_config_data.append(data1) # configure the cb to route data from additional core to the # main core if add_additional_core: input_port_name = f"data_in_{bit_width}b" output_port_name = f"data_out_{bit_width}b_extra" additional_in_port_node = \ tile_circuit.tiles[bit_width].ports[input_port_name] additional_out_port_node = \ tile_circuit.tiles[bit_width].ports[output_port_name] data2 = tile_circuit.get_route_bitstream_config( additional_out_port_node, additional_in_port_node) raw_config_data.append(data2) in_sb_name = create_name(str(in_sb_node)) out_sb_name = create_name(str(out_sb_node)) test_data.append( (circuit.interface.ports[in_sb_name], circuit.interface.ports[out_sb_name], fault.random.random_bv(bit_width), in_sb_node)) if add_additional_core: assert len(raw_config_data) / 3 == len(test_data) else: assert len(raw_config_data) / 2 == len(test_data) # process the raw config data and change it into the actual config addr for reg_addr, feat_addr, config_value in raw_config_data: reg_addr = reg_addr << tile_circuit.feature_config_slice.start feat_addr = feat_addr << tile_circuit.tile_id_width addr = reg_addr | feat_addr addr = BitVector[data_width](addr) | BitVector[data_width](tile_id) config_data.append((addr, config_value)) # actual tests tester = BasicTester(circuit, circuit.clk, circuit.reset) tester.poke(circuit.tile_id, tile_id) stride = 3 if add_additional_core else 2 for i in range(0, len(config_data), stride): tester.reset() c_data = config_data[i:i + stride] c_data = compress_config_data(c_data) for addr, config_value in c_data: tester.configure(addr, config_value) tester.configure(addr, config_value + 1, False) tester.config_read(addr) tester.eval() tester.expect(circuit.read_config_data, config_value) input_port, output_port, value, in_node = test_data[i // stride] tester.poke(input_port, value) tester.eval() # add additional error to check, i.e. sending random junk data to # all unrelated ports for bit_width in bit_widths: sb = tile_circuit.sbs[bit_width] sbs = sb.switchbox.get_all_sbs() for sb in sbs: if sb == in_node or sb.io == SwitchBoxIO.SB_OUT: continue port_name = create_name(str(sb)) port = circuit.interface.ports[port_name] tester.poke(port, fault.random.random_bv(bit_width)) tester.eval() tester.expect(output_port, value) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def dummy_col(_: int, __: int): return DummyCore()