def master(dut, port, num): # Choose operation types based on port mode ops_choice = { "both": ["w", "r"], "write": ["w"], "read": ["r"], }[port.mode] driver = NativePortDriver(port) for i in range(n_ops): bank = prng.randrange(n_banks) # We will later distinguish data by its row address row = num col = 0x20 * num + i addr = dut.addr_port(bank=bank, row=row, col=col) addr_iface = dut.addr_iface(row=row, col=col) if prng.choice(ops_choice) == "w": yield from driver.write(addr, data=i) produced[num].append(self.W(bank, addr_iface, data=i, we=0xff)) else: yield from driver.read(addr) produced[num].append(self.R(bank, addr_iface, data=None)) for _ in range(8): yield
def crossbar_stress_test(self, dut, ports, n_banks, n_ops, clocks=None): # Runs simulation with multiple masters writing and reading to multiple banks controller = ControllerStub( dut.interface, write_latency=dut.settings.phy.write_latency, read_latency=dut.settings.phy.read_latency) # Store data produced per master produced = defaultdict(list) prng = random.Random(42) def master(dut, driver, num): # Choose operation types based on port mode ops_choice = { "both": ["w", "r"], "write": ["w"], "read": ["r"], }[driver.port.mode] for i in range(n_ops): bank = prng.randrange(n_banks) # We will later distinguish data by its row address row = num col = 0x20 * num + i addr = dut.addr_port(bank=bank, row=row, col=col) addr_iface = dut.addr_iface(row=row, col=col) if prng.choice(ops_choice) == "w": yield from driver.write(addr, data=i) produced[num].append( self.W(bank, addr_iface, data=i, we=0xff)) else: yield from driver.read(addr) produced[num].append(self.R(bank, addr_iface, data=None)) yield from driver.wait_all() generators = defaultdict(list) for i, port in enumerate(ports): driver = NativePortDriver(port) generators[port.clock_domain].append(master(dut, driver, i)) generators[port.clock_domain].extend(driver.generators()) generators["sys"] += controller.generators() generators["sys"].append(timeout_generator(80 * n_ops)) sim_kwargs = {} if clocks is not None: sim_kwargs["clocks"] = clocks run_simulation(dut, generators, **sim_kwargs) # Split controller data by master, as this is what we want to compare consumed = defaultdict(list) for data in controller.data: master = data.addr >> (dut.settings.geom.colbits - dut.address_align) if isinstance(data, self.R): # Master couldn't know the data when it was sending data = data._replace(data=None) consumed[master].append(data) return produced, consumed, controller.data
def producer(dut, port): driver = NativePortDriver(port) for t in transfers: addr = dut.addr_port(bank=t["bank"], row=t["row"], col=t["col"]) if t["rw"] == self.W: yield from driver.write(addr, data=t["data"], we=t.get("we", None)) elif t["rw"] == self.R: data = (yield from driver.read(addr)) reads.append(data) else: raise TypeError(t["rw"])
def master_a(dut, port): driver = NativePortDriver(port) adr = functools.partial(dut.addr_port, row=1, col=1) write = functools.partial(driver.write, wait_data=False) yield from write(adr(bank=0), data=0x10) yield from write(adr(bank=1), data=0x11) yield from write(adr(bank=0), data=0x12, wait_data=True)
def test_address_mappings(self): # Verify that address is translated correctly. reads = [] def producer(dut, driver): for t in transfers: addr = dut.addr_port(bank=t["bank"], row=t["row"], col=t["col"]) if t["rw"] == self.W: yield from driver.write(addr, data=t["data"], we=t.get("we", None)) elif t["rw"] == self.R: data = (yield from driver.read(addr)) reads.append(data) else: raise TypeError(t["rw"]) geom_settings = dict(colbits=10, rowbits=13, bankbits=2) dut = CrossbarDUT(geom_settings=geom_settings) port = dut.crossbar.get_port() driver = NativePortDriver(port) transfers = [ dict(rw=self.W, bank=2, row=0x30, col=0x03, data=0x20), dict(rw=self.W, bank=3, row=0x30, col=0x03, data=0x21), dict(rw=self.W, bank=2, row=0xab, col=0x03, data=0x22), dict(rw=self.W, bank=2, row=0x30, col=0x13, data=0x23), dict(rw=self.R, bank=1, row=0x10, col=0x99), dict(rw=self.R, bank=0, row=0x10, col=0x99), dict(rw=self.R, bank=1, row=0xcd, col=0x99), dict(rw=self.R, bank=1, row=0x10, col=0x77), ] expected = [] read_data = ControllerStub.read_data_counter() for i, t in enumerate(transfers): cls = t["rw"] addr = dut.addr_iface(row=t["row"], col=t["col"]) if cls == self.W: kwargs = dict(data=t["data"], we=0xff) elif cls == self.R: kwargs = dict(data=next(read_data)) expected.append(cls(bank=t["bank"], addr=addr, **kwargs)) data = self.crossbar_test(dut, [producer(dut, driver)] + driver.generators()) self.assertEqual(data, expected)
def master_b(dut, port): driver = NativePortDriver(port) adr = functools.partial(dut.addr_port, row=2, col=2) read = functools.partial(driver.read, wait_data=False) yield from read(adr(bank=1)) yield from read(adr(bank=1)) yield from read(adr(bank=1)) yield from read(adr(bank=1)) yield from read(adr(bank=1))
def master_b(dut, port): driver = NativePortDriver(port) adr = functools.partial(dut.addr_port, row=2, col=2) write = functools.partial(driver.write, wait_data=False) yield from write(adr(bank=1), data=0x20) yield from write(adr(bank=1), data=0x21) yield from write(adr(bank=1), data=0x22) yield from write(adr(bank=1), data=0x23) yield from write(adr(bank=1), data=0x24)
def master_a(dut, port): driver = NativePortDriver(port) adr = functools.partial(dut.addr_port, row=1, col=1) read = functools.partial(driver.read, wait_data=False) yield from read(adr(bank=0)) yield from read(adr(bank=1)) yield from read(adr(bank=0)) # Wait for read data to show up for _ in range(16): yield
def test_arbitration(self): # Create multiple masters that write to the same bank at the same time and verify that all # the requests have been sent correctly. def producer(dut, driver, num): addr = dut.addr_port(bank=3, row=0x10 + num, col=0x20 + num) yield from driver.write(addr, data=0x30 + num) dut = CrossbarDUT() ports = [dut.crossbar.get_port() for _ in range(4)] drivers = [NativePortDriver(port) for port in ports] masters = [ producer(dut, driver, i) for i, driver in enumerate(drivers) ] generators = masters for driver in drivers: generators.extend(driver.generators()) data = self.crossbar_test(dut, generators) expected = { self.W(bank=3, addr=dut.addr_iface(row=0x10, col=0x20), data=0x30, we=0xff), self.W(bank=3, addr=dut.addr_iface(row=0x11, col=0x21), data=0x31, we=0xff), self.W(bank=3, addr=dut.addr_iface(row=0x12, col=0x22), data=0x32, we=0xff), self.W(bank=3, addr=dut.addr_iface(row=0x13, col=0x23), data=0x33, we=0xff), } self.assertEqual(set(data), expected)
def test_lock_write(self): # Verify that the locking mechanism works # Create a situation when one master A wants to write to banks 0 then 1, but master B is # continuously writing to bank 1 (bank is locked) so that master A is blocked. We use # wait_data=False because we are only concerned about sending commands fast enough for # the lock to be held continuously. def master_a(dut, driver): adr = functools.partial(dut.addr_port, row=1, col=1) write = functools.partial(driver.write, wait_data=False) yield from write(adr(bank=0), data=0x10) yield from write(adr(bank=1), data=0x11) yield from write(adr(bank=0), data=0x12, wait_data=True) def master_b(dut, driver): adr = functools.partial(dut.addr_port, row=2, col=2) write = functools.partial(driver.write, wait_data=False) yield from write(adr(bank=1), data=0x20) yield from write(adr(bank=1), data=0x21) yield from write(adr(bank=1), data=0x22) yield from write(adr(bank=1), data=0x23) yield from write(adr(bank=1), data=0x24) dut = CrossbarDUT() ports = [dut.crossbar.get_port() for _ in range(2)] drivers = [NativePortDriver(port) for port in ports] masters = [master_a(dut, drivers[0]), master_b(dut, drivers[1])] data = self.crossbar_test( dut, masters + drivers[0].generators() + drivers[1].generators()) expected = [ self.W(bank=0, addr=dut.addr_iface(row=1, col=1), data=0x10, we=0xff), # A self.W(bank=1, addr=dut.addr_iface(row=2, col=2), data=0x20, we=0xff), # B self.W(bank=1, addr=dut.addr_iface(row=2, col=2), data=0x21, we=0xff), # B self.W(bank=1, addr=dut.addr_iface(row=2, col=2), data=0x22, we=0xff), # B self.W(bank=1, addr=dut.addr_iface(row=2, col=2), data=0x23, we=0xff), # B self.W(bank=1, addr=dut.addr_iface(row=2, col=2), data=0x24, we=0xff), # B self.W(bank=1, addr=dut.addr_iface(row=1, col=1), data=0x11, we=0xff), # A self.W(bank=0, addr=dut.addr_iface(row=1, col=1), data=0x12, we=0xff), # A ] self.assertEqual(data, expected)
def producer(dut, port, num): driver = NativePortDriver(port) addr = dut.addr_port(bank=3, row=0x10 + num, col=0x20 + num) yield from driver.write(addr, data=0x30 + num)