def __get_config_bits(self, lo: int, hi: int): assert hi > lo assert lo >= 0 assert hi <= (len(self.__config) * CONFIG_DATA_WIDTH) start = math.floor(lo / 32) end = math.floor((hi - 1) / 32) lo_int = lo % CONFIG_DATA_WIDTH hi_int = hi % CONFIG_DATA_WIDTH if start == end: return self.__config[start][lo_int:hi_int] ret = self.__config[start][lo_int:CONFIG_DATA_WIDTH] for i in range(start + 1, end): ret = BitVector.concat(ret, self.__config[i]) ret = BitVector.concat(ret, self.__config[i][0:hi_int]) assert ret.num_bits == (hi - lo) return ret
def test_concat(): a = BitVector(4, 4) b = BitVector(1, 4) c = BitVector.concat(a, b) expected = BitVector([1, 0, 0, 0, 0, 0, 1, 0]) assert expected == c
def test_tile(): core = DummyCore() tile = Tile(core) tile_circ = tile.circuit() # No functional model for tile yet, so we have to use the # standard fault tester for now tester = BasicTester(tile_circ, tile_circ.clk, tile_circ.reset) # assign the tile a random ID for configuration tile_id = random_bv(16) tester.poke(tile_circ.tile_id, tile_id) tester.reset() # Connect random vals to all tile inputs inputs_applied = {} for side_in in (tile_circ.north.I, tile_circ.south.I, tile_circ.east.I, tile_circ.west.I): for i in range(len(side_in.layer1)): port = side_in.layer1[i] rand_input = random_bv(1) inputs_applied[port] = rand_input tester.poke(port, rand_input) for j in range(len(side_in.layer16)): port = side_in.layer16[j] rand_input = random_bv(16) inputs_applied[port] = rand_input tester.poke(port, rand_input) # Write to all configuration registers in the tile # This test should be applicapable to any tile, regardless # of the core it's using data_written = {} for i, feat in enumerate(tile.features()): feat_addr = BitVector(i, 8) for reg in feat.registers.values(): reg_addr = BitVector(reg.addr, 8) upper_config_addr = BitVector.concat(reg_addr, feat_addr) config_addr = BitVector.concat(upper_config_addr, tile_id) # Ensure the register is wide enough to contain the random value rand_data = random_bv(reg.width) # Further restrict random config data values based on feature # Only 0-3 valid for SB config_data if (feat == tile.sb): if ((reg_addr % 2) == 0): rand_data = rand_data % 4 # Only 0-1 valid for SB regs else: rand_data = rand_data % 2 # Only 0-9 valid for CB config_data elif (feat in tile.cbs): rand_data = rand_data % 10 # Make sure we pass 32 bits of config data to configure config_data = BitVector(rand_data, 32) tester.configure(config_addr, config_data) # Keep track of data written so we know what to expect to read back data_written[config_addr] = config_data # Now, read back all the configuration we just wrote for addr in data_written: tester.config_read(addr) expected_data = data_written[addr] tester.expect(tile_circ.read_config_data, expected_data) feat_addr = addr[16:24] reg_addr = addr[24:32] check_all_config(tester, tile_circ, tile, data_written, inputs_applied) # Try writing to tile with wrong tile id for config_addr in data_written: new_tile_id = config_addr[0:16] + 1 upper_config_addr = config_addr[16:32] new_config_addr = BitVector.concat(upper_config_addr, new_tile_id) random_data = random_bv(32) tester.configure(new_config_addr, random_data) # Read all the config back again to make sure nothing changed check_all_config(tester, tile_circ, tile, data_written, inputs_applied) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def gen_cb(width: int, num_tracks: int, feedthrough_outputs: str, has_constant: bool, default_value: int): CONFIG_ADDR_WIDTH = 32 CONFIG_DATA_WIDTH = 32 # The names of the inputs to the module are given by @feedthrough_outputs. inputs = ["in_" + str(i) for i, c in enumerate(feedthrough_outputs) if c == "1"] # The number of total config bits needed is the number of bits needed to # select between all the inputs (including the constant if it is included), # as well as a constant value if @has_constant is True. Consequently, the # number of config registers needed is the total number of config bits # needed, divided by the size of a single config data element (e.g. 32 # bits). mux_height = len(inputs) if has_constant: mux_height += 1 mux_sel_bits = math.ceil(math.log(mux_height, 2)) config_bits_needed = mux_sel_bits if has_constant: config_bits_needed += width num_config_regs = math.ceil(config_bits_needed / CONFIG_DATA_WIDTH) reset_val = num_tracks - feedthrough_outputs.count("0") + has_constant - 1 reset_val = BitVector(reset_val, mux_sel_bits) if has_constant: reset_val = BitVector.concat(BitVector(default_value, width), reset_val) reset_val = BitVector(reset_val, CONFIG_DATA_WIDTH) class _CB: def __init__(self): self.__config = [BitVector(0, CONFIG_DATA_WIDTH) for _ in range(num_config_regs)] self.reset() def reset(self): self.configure(BitVector(0, CONFIG_ADDR_WIDTH), reset_val) self.out = fault.UnknownValue self.read_data = fault.UnknownValue def configure(self, addr: BitVector, data: BitVector): assert addr.num_bits == CONFIG_ADDR_WIDTH assert data.num_bits == CONFIG_ADDR_WIDTH addr_high_bits = addr[24:32] config_reg_select = addr_high_bits.as_uint() if config_reg_select in range(num_config_regs): self.__config[config_reg_select] = data # Function to slice the global config bit space. Returns bits in the # range [lo, hi). def __get_config_bits(self, lo: int, hi: int): assert hi > lo assert lo >= 0 assert hi <= (len(self.__config) * CONFIG_DATA_WIDTH) start = math.floor(lo / 32) end = math.floor((hi - 1) / 32) lo_int = lo % CONFIG_DATA_WIDTH hi_int = hi % CONFIG_DATA_WIDTH if start == end: return self.__config[start][lo_int:hi_int] ret = self.__config[start][lo_int:CONFIG_DATA_WIDTH] for i in range(start + 1, end): ret = BitVector.concat(ret, self.__config[i]) ret = BitVector.concat(ret, self.__config[i][0:hi_int]) assert ret.num_bits == (hi - lo) return ret def __call__(self, *args): assert len(args) == len(inputs) select = self.__get_config_bits(0, mux_sel_bits) select_as_uint = select.as_uint() # TODO: read_data logic if select_as_uint in range(len(inputs)): self.out = args[select_as_uint] return self.out if has_constant: return self.__get_config_bits(mux_sel_bits, mux_sel_bits + width) else: return BitVector(0, width) # Debug method to read config data. @property def config(self): return self.__config return _CB