예제 #1
0
    def test_cas_tccd(self):
        # Verify tCCD.
        def main_generator(dut):
            yield from dut.bm_drivers[2].read()
            yield from dut.bm_drivers[3].read()
            ready = {2: dut.bank_machines[2].cmd.ready, 3: dut.bank_machines[3].cmd.ready}

            # Wait for activate
            while not ((yield ready[2]) or (yield ready[3])):
                yield
            # Invalidate command that was ready
            if (yield ready[2]):
                yield from dut.bm_drivers[2].nop()
            else:
                yield from dut.bm_drivers[3].nop()
            yield

            # Wait for the second activate; start from 1 for the previous cycle
            cas_time = 1
            while not ((yield ready[2]) or (yield ready[3])):
                cas_time += 1
                yield

            self.assertEqual(cas_time, 3)

        dut = MultiplexerDUT(timing_settings=dict(tCCD=3))
        generators = [
            main_generator(dut),
            timeout_generator(50),
        ]
        run_simulation(dut, generators)
예제 #2
0
    def test_fsm_anti_starvation(self):
        # Check that anti-starvation works according to controller settings.
        def main_generator(dut):
            yield from dut.bm_drivers[2].read()
            yield from dut.bm_drivers[3].write()

            # Go to WRITE
            # anti starvation does not work for 1st read, as read_time_en already starts as 1
            # READ -> RTW -> WRITE
            while (yield from dut.fsm_state()) != "WRITE":
                yield

            # wait for write anti starvation
            for _ in range(dut.settings.write_time):
                self.assertEqual((yield from dut.fsm_state()), "WRITE")
                yield
            self.assertEqual((yield from dut.fsm_state()), "WTR")

            # WRITE -> WTR -> READ
            while (yield from dut.fsm_state()) != "READ":
                yield

            # Wait for read anti starvation
            for _ in range(dut.settings.read_time):
                self.assertEqual((yield from dut.fsm_state()), "READ")
                yield
            self.assertEqual((yield from dut.fsm_state()), "RTW")

        dut = MultiplexerDUT()
        generators = [
            main_generator(dut),
            timeout_generator(100),
        ]
        run_simulation(dut, generators)
예제 #3
0
    def test_steer_write_correct_phases(self):
        # Check that correct phases are being used during WRITE.
        def main_generator(dut):
            yield from dut.bm_drivers[2].write()
            yield from dut.bm_drivers[3].activate()

            while not (yield dut.bank_machines[2].cmd.ready):
                yield
            yield

            # fsm starts in READ
            for phase in range(dut.settings.phy.nphases):
                if phase == dut.settings.phy.wrphase:
                    self.assertEqual((yield dut.dfi.phases[phase].bank), 2)
                elif phase == dut.settings.phy.wrcmdphase:
                    self.assertEqual((yield dut.dfi.phases[phase].bank), 3)
                else:
                    self.assertEqual((yield dut.dfi.phases[phase].bank), 0)

        dut = MultiplexerDUT()
        generators = [
            main_generator(dut),
            timeout_generator(50),
        ]
        run_simulation(dut, generators)
예제 #4
0
    def test_single_phase_cmd_req(self):
        # Verify that, for a single phase, commands are sent sequentially.
        def main_generator(dut):
            yield from dut.bm_drivers[2].write()
            yield from dut.bm_drivers[3].activate()
            ready = {2: dut.bank_machines[2].cmd.ready, 3: dut.bank_machines[3].cmd.ready}

            # Activate should appear first
            while not ((yield ready[2]) or (yield ready[3])):
                yield
            yield from dut.bm_drivers[3].nop()
            yield
            self.assertEqual((yield dut.dfi.phases[0].bank), 3)

            # Then write
            while not (yield ready[2]):
                yield
            yield from dut.bm_drivers[2].nop()
            yield
            self.assertEqual((yield dut.dfi.phases[0].bank), 2)

        dut = MultiplexerDUT(phy_settings=dict(nphases=1))
        generators = [
            main_generator(dut),
            timeout_generator(50),
        ]
        run_simulation(dut, generators)
예제 #5
0
    def test_fsm_write_to_read_latency(self):
        # Verify the timing of WRITE to READ transition.
        def main_generator(dut):
            write_latency = math.ceil(dut.settings.phy.cwl / dut.settings.phy.nphases)
            wtr = dut.settings.timing.tWTR + write_latency + dut.settings.timing.tCCD or 0

            expected = "w" + (wtr - 1) * ">" + "r"
            states   = ""

            # Simulate until we are in WRITE
            yield from dut.bm_drivers[0].write()
            while (yield from dut.fsm_state()) != "WRITE":
                yield

            # Set read_available=1
            yield from dut.bm_drivers[0].read()
            yield

            for _ in range(len(expected)):
                state = (yield from dut.fsm_state())
                states += {
                    "READ": "r",
                    "WRITE": "w",
                }.get(state, ">")
                yield

            self.assertEqual(states, expected)

        dut = MultiplexerDUT()
        generators = [
            main_generator(dut),
            timeout_generator(50),
        ]
        run_simulation(dut, generators)
예제 #6
0
    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
예제 #7
0
 def crossbar_test(self, dut, generators, timeout=100, **kwargs):
     # Runs simulation with a controller stub (passive generators) and user generators
     if not isinstance(generators, list):
         generators = [generators]
     controller = ControllerStub(dut.interface,
                                 write_latency=dut.settings.phy.write_latency,
                                 read_latency=dut.settings.phy.read_latency,
                                 **kwargs)
     generators += [*controller.generators(), timeout_generator(timeout)]
     run_simulation(dut, generators)
     return controller.data
예제 #8
0
    def bankmachine_commands_test(self, dut, requests, generators=None):
        # Perform a test by simulating requests producer and return registered commands
        commands = []

        def producer(dut):
            for req in requests:
                yield dut.bankmachine.req.addr.eq(req["addr"])
                yield dut.bankmachine.req.we.eq(req["we"])
                yield dut.bankmachine.req.valid.eq(1)
                yield
                while not (yield dut.bankmachine.req.ready):
                    yield
                yield dut.bankmachine.req.valid.eq(0)
                for _ in range(req.get("delay", 0)):
                    yield

        def req_consumer(dut):
            for req in requests:
                if req["we"]:
                    signal = dut.bankmachine.req.wdata_ready
                else:
                    signal = dut.bankmachine.req.rdata_valid
                while not (yield signal):
                    yield
                yield

        @passive
        def cmd_consumer(dut):
            while True:
                while not (yield dut.bankmachine.cmd.valid):
                    yield
                yield dut.bankmachine.cmd.ready.eq(1)
                yield
                commands.append((yield from dut.get_cmd()))
                yield dut.bankmachine.cmd.ready.eq(0)
                yield

        all_generators = [
            producer(dut),
            req_consumer(dut),
            cmd_consumer(dut),
            timeout_generator(50 * len(requests)),
        ]
        if generators is not None:
            all_generators += [g(dut) for g in generators]
        run_simulation(dut, all_generators)
        return commands
예제 #9
0
    def test_write_datapath(self):
        # Verify that data is transmitted from native interface to DFI.
        def main_generator(dut):
            yield from dut.bm_drivers[2].write()
            # 16bits * 2 (DDR) * 1 (phases)
            yield dut.interface.wdata.eq(0xbaadf00d)
            yield dut.interface.wdata_we.eq(0xf)

            while not (yield dut.bank_machines[2].cmd.ready):
                yield
            yield

            self.assertEqual((yield dut.dfi.phases[0].wrdata), 0xbaadf00d)
            self.assertEqual((yield dut.dfi.phases[0].wrdata_en), 1)
            self.assertEqual((yield dut.dfi.phases[0].address), 2)
            self.assertEqual((yield dut.dfi.phases[0].bank), 2)

        dut = MultiplexerDUT(phy_settings=dict(nphases=1))
        generators = [
            main_generator(dut),
            timeout_generator(50),
        ]
        run_simulation(dut, generators)
예제 #10
0
    def test_correct_period_length(self):
        # Verify that period length is correct by measuring time between CSR changes.
        period_bits = 5
        period = 2**period_bits

        n_per_period = {0: 3, 1: 6, 2: 9}
        timeline = {}
        for p, n in n_per_period.items():
            for i in range(n):
                margin = 10
                timeline[period * p + margin + i] = "write"

        def main_generator(dut):
            # Keep the values always up to date
            yield dut.bandwidth.update.re.eq(1)

            # Wait until we have the data from 1st period
            while (yield dut.bandwidth.nwrites.status) != 3:
                yield

            # Count time to next period
            cycles = 0
            while (yield dut.bandwidth.nwrites.status) != 6:
                cycles += 1
                yield

            self.assertEqual(cycles, period)

        dut = BandwidthDUT(period_bits=period_bits)
        cmd_driver = CommandDriver(dut.cmd)
        generators = [
            main_generator(dut),
            cmd_driver.timeline_generator(timeline.items()),
            timeout_generator(period * 3),
        ]
        run_simulation(dut, generators)
예제 #11
0
    def test_requests_from_multiple_bankmachines(self):
        # Check complex communication scenario with requests from multiple bank machines
        # The communication is greatly simplified - data path is completely ignored, no responses
        # from PHY are simulated. Each bank machine performs a sequence of requests, bank machines
        # are ordered randomly and the DFI command data is checked to verify if all the commands
        # have been sent if correct per-bank order.

        # Tequests sequence on given bank machines
        bm_sequences = {
            0: "awwwwwwp",
            1: "arrrrrrp",
            2: "arwrwrwp",
            3: "arrrwwwp",
            4: "awparpawp",
            5: "awwparrrrp",
        }
        # convert to lists to use .pop()
        bm_sequences = {bm_num: list(seq) for bm_num, seq in bm_sequences.items()}

        def main_generator(bank_machines, drivers):
            # work on a copy
            bm_seq = copy.deepcopy(bm_sequences)

            def non_empty():
                return list(filter(lambda n: len(bm_seq[n]) > 0, bm_seq.keys()))

            # Artificially perform the work of LiteDRAMCrossbar by always picking only one request
            prng = random.Random(42)
            while len(non_empty()) > 0:
                # Pick random bank machine
                bm_num = prng.choice(non_empty())

                # Set given request
                request_char = bm_seq[bm_num].pop(0)
                yield from drivers[bm_num].request(request_char)
                yield

                # Wait for ready
                while not (yield bank_machines[bm_num].cmd.ready):
                    yield

                # Disable it
                yield from drivers[bm_num].nop()

            for _ in range(16):
                yield

        # Gather data on DFI
        DFISnapshot = namedtuple("DFICapture",
                                 ["cmd", "bank", "address", "wrdata_en", "rddata_en"])
        dfi_snapshots = []

        @passive
        def dfi_monitor(dfi):
            while True:
                # Capture current state of DFI lines
                phases = []
                for i, p in enumerate(dfi.phases):
                    # Transform cas/ras/we to command name
                    cas_n, ras_n, we_n = (yield p.cas_n), (yield p.ras_n), (yield p.we_n)
                    captured = {"cmd": dfi_cmd_to_char(cas_n, ras_n, we_n)}

                    # Capture rest of fields
                    for field in DFISnapshot._fields:
                        if field != "cmd":
                            captured[field] = (yield getattr(p, field))

                    phases.append(DFISnapshot(**captured))
                dfi_snapshots.append(phases)
                yield

        dut = MultiplexerDUT()
        generators = [
            main_generator(dut.bank_machines, dut.bm_drivers),
            dfi_monitor(dut.dfi),
            timeout_generator(200),
        ]
        run_simulation(dut, generators)

        # Check captured DFI data with the description
        for snap in dfi_snapshots:
            for i, phase_snap in enumerate(snap):
                if phase_snap.cmd == "_":
                    continue

                # Distinguish bank machines by the bank number
                bank = phase_snap.bank
                # Find next command for the given bank
                cmd = bm_sequences[bank].pop(0)

                # Check if the captured data is correct
                self.assertEqual(phase_snap.cmd, cmd)
                if cmd in ["w", "r"]:
                    # Addresses are artificially forced to bank numbers in drivers
                    self.assertEqual(phase_snap.address, bank)
                    if cmd == "w":
                        self.assertEqual(phase_snap.wrdata_en, 1)
                    if cmd == "r":
                        self.assertEqual(phase_snap.rddata_en, 1)