def test_counter(): """Simple testbench for the Counter above.""" # Create a counter with a rollover at 18. # This awkward non-power-of-two value will help test that we're not # rolling over by accident of the bit width of the counter. counter = Counter(limit=18) # Test benches are written as Python generators, which yield # commands to the simulator such as "send me the current value of this # signal" or "advance the simulation by one clock cycle". def testbench(): for step in range(20): # Check outputs are correct at each step. assert (yield counter.counter) == (step % 18) if step == 17: assert (yield counter.rollover) else: assert not (yield counter.rollover) # Advance simulation by one cycle. yield sim = Simulator(counter) # To test synchronous processes, we create a clock at some nominal # frequency (which only changes the displayed timescale in the output), # and add our testbench as a "synchronous" process. sim.add_clock(1 / 10e6) sim.add_sync_process(testbench) sim.run()
class TestBase(unittest.TestCase): """Base class for testing an Amaranth module. The module can use sync, comb or both. """ def setUp(self): # Ensure IS_SIM_RUN set global _IS_SIM_RUN _IS_SIM_RUN = True # Create DUT and add to simulator self.m = Module() self.dut = self.create_dut() self.m.submodules['dut'] = self.dut self.m.submodules['dummy'] = _DummySyncModule() self.sim = Simulator(self.m) def create_dut(self): """Returns an instance of the device under test""" raise NotImplementedError def add_process(self, process): """Add main test process to the simulator""" self.sim.add_sync_process(process) def add_sim_clocks(self): """Add clocks as required by sim. """ self.sim.add_clock(1, domain='sync') def run_sim(self, process, write_trace=False): self.add_process(process) self.add_sim_clocks() if write_trace: with self.sim.write_vcd("zz.vcd", "zz.gtkw"): self.sim.run() # Discourage commiting code with tracing active self.fail("Simulation tracing active. " "Turn off after debugging complete.") else: self.sim.run()
def basic_vm_test(): cpu = MtkCpu( mem_config=EBRMemConfig( mem_size_words=0x123, mem_content_words=None, # Optional[List[int]] mem_addr=CODE_START_ADDR, simulate=True, ) ) def wait_decode() -> int: # returns pc of instr being decoded while True: is_decode = yield cpu.fetch if is_decode: return (yield cpu.pc) yield def wait_pc(target_pc : int, instr_limit: Optional[int]=None): import logging logging.info(f"== waiting for pc {target_pc}") miss = 0 while True: pc = wait_decode() logging.info(f"pc: {pc}") if pc == target_pc: logging(f"pc {target_pc} found") else: miss += 1 if miss == instr_limit: raise ValueError(f"limit of {instr_limit} instructions exceeded while waiting for {target_pc}") # TODO test is not finished. def main(): yield cpu.current_priv_mode.eq(PrivModeBits.USER) yield cpu.csr_unit.satp.eq(0x8000_0123) res = [] for _ in range (30): priv = yield cpu.current_priv_mode priv = yield cpu.arbiter.addr_translation_en priv = yield cpu.csr_unit.satp.mode priv = yield cpu.pc res.append(hex(priv)) yield raise ValueError(res) sim = Simulator(cpu) sim.add_clock(1e-6) sim.add_sync_process(main) from mtkcpu.utils.tests.sim_tests import get_state_name, find_fsm # fsm = find_fsm(cpu.arbiter, "fsm") # main_fsm = find_fsm(cpu, "fsm") # raise ValueError(fsm) traces = [ sim._fragment.domains["sync"].clk, cpu.pc, cpu.arbiter.addr_translation_en, cpu.arbiter.translation_ack, cpu.arbiter.start_translation, # fsm.state, cpu.arbiter.pe.i, cpu.arbiter.pe.o, cpu.arbiter.pe.none, cpu.csr_unit.satp.mode, cpu.csr_unit.satp.ppn, cpu.arbiter.first, cpu.arbiter.error_code, # main_fsm.state ] with sim.write_vcd(f"vm.vcd", "vm.gtkw", traces=traces): sim.run()
def reg_test( name: str, timeout_cycles: Optional[int], reg_num: int, expected_val: Optional[int], expected_mem: Optional[MemoryContents], reg_init: RegistryContents, mem_cfg: EBRMemConfig, verbose: bool = False, ): cpu = MtkCpu( reg_init=reg_init.reg, with_debug=False, # XXX mem_config=mem_cfg ) sim = Simulator(cpu) sim.add_clock(1e-6) assert (reg_num is None and expected_val is None) or ( reg_num is not None and expected_val is not None ) # Since Amaranth HDL's 'Memory' instance simulation in included, # we don't need to use custom implementation (however some coverage drop # is present - 'Memory' class is assumed single-cycle-access, while # 'get_sim_memory_test' processing delay is random). # sim.add_sync_process(get_sim_memory_test(cpu=cpu, mem_dict=mem_dict)) # instead only collect write transactions directly on a bus. result_mem = {} sim.add_sync_process(capture_write_transactions(cpu=cpu, dict_reference=result_mem)) sim.add_sync_process( get_sim_register_test( name=name, cpu=cpu, reg_num=reg_num, expected_val=expected_val, timeout_cycles=timeout_cycles, ) ) csr_unit : CsrUnit = cpu.csr_unit # frag = Fragment.get(cpu, platform=None) # main_fsm = frag.find_generated("fsm") e = cpu.exception_unit sim_traces = [ # main_fsm.state, # e.m_instruction, # e.mtval.value, # csr_unit.mtvec.base, # csr_unit.mtvec.mode, # *csr_unit.mepc.fields.values(), *csr_unit.mcause.fields.values(), *csr_unit.satp.fields.values(), # *csr_unit.mie.fields.values(), # *csr_unit.mstatus.fields.values(), # *csr_unit.mtime.fields.values(), # *csr_unit.mtimecmp.fields.values(), cpu.instr, cpu.pc, # csr_unit.rs1, # csr_unit.csr_idx, # csr_unit.rd, # csr_unit.rd_val, # csr_unit.vld, # csr_unit.ONREAD, # csr_unit.ONWRITE, cpu.arbiter.pe.i, cpu.arbiter.pe.o, cpu.arbiter.pe.none, cpu.arbiter.bus_free_to_latch, cpu.arbiter.error_code, cpu.arbiter.addr_translation_en, cpu.arbiter.translation_ack, cpu.arbiter.start_translation, cpu.arbiter.phys_addr, cpu.arbiter.root_ppn, *cpu.arbiter.pte.fields.values(), cpu.arbiter.generic_bus.addr, cpu.arbiter.generic_bus.read_data, cpu.arbiter.vpn, ] # from amaranth.back import verilog # s = verilog.convert(cpu) # open("cpu.v", "w").write(s) with sim.write_vcd("cpu.vcd", "cpu.gtkw", traces=sim_traces): sim.run() if expected_mem is not None: MemoryContents(result_mem).assert_equality(expected_mem)
def create_jtag_simulator(cpu): # cursed stuff for retrieving jtag FSM state for 'traces=vcd_traces' variable # https://freenode.irclog.whitequark.org/amaranth/2020-07-26#27592720; frag = Fragment.get(cpu, platform=None) jtag_fsm = frag.find_generated("debug", "jtag", "fsm") sim = Simulator(frag) sim.add_clock(1e-6) jtag_fsm_sig = jtag_fsm.state main_clk_sig = sim._fragment.domains["sync"].clk jtag_loc = cpu.debug.jtag dmcontrol_r = cpu.debug.dmi_regs[DMIReg.DMCONTROL].r.fields.values() dmcontrol_w = cpu.debug.dmi_regs[DMIReg.DMCONTROL].w.fields.values() hartinfo_r = cpu.debug.dmi_regs[DMIReg.HARTINFO].r.fields.values() hartinfo_w = cpu.debug.dmi_regs[DMIReg.HARTINFO].w.fields.values() abstracts_r = cpu.debug.dmi_regs[DMIReg.ABSTRACTS].r.fields.values() abstracts_w = cpu.debug.dmi_regs[DMIReg.ABSTRACTS].w.fields.values() dmstatus_r = cpu.debug.dmi_regs[DMIReg.DMSTATUS].r.fields.values() dmstatus_w = cpu.debug.dmi_regs[DMIReg.DMSTATUS].w.fields.values() command_w = cpu.debug.dmi_regs[DMIReg.COMMAND].w.fields.values() command_r = cpu.debug.dmi_regs[DMIReg.COMMAND].r.fields.values() data0_w = cpu.debug.dmi_regs[DMIReg.DATA0].w.fields.values() data0_r = cpu.debug.dmi_regs[DMIReg.DATA0].r.fields.values() vcd_traces = [ cpu.debug.dmi_op, cpu.debug.dmi_address, cpu.debug.dmi_data, # jtag_loc.regs[JtagIR.DMI].update, # jtag_loc.regs[JtagIR.DMI].capture, # jtag_loc.DATA_WRITE, # jtag_loc.DATA_READ, # jtag_loc.DMI_WRITE, cpu.mtime, cpu.debug.ONWRITE, cpu.debug.ONREAD, # cpu.debug.HANDLER, cpu.debug.DBG_DMI_ADDR, jtag_loc.BAR, *data0_r, # *dmcontrol_r, # jtag_loc.BAR, # *dmcontrol_w, # jtag_loc.BAR, # *dmcontrol_r, # jtag_loc.BAR, # jtag_loc.regs[JtagIR.DMI].r.op, # jtag_loc.BAR, # *dmstatus_r, # *hartinfo_w, jtag_loc.BAR, *abstracts_w, jtag_loc.BAR, *abstracts_r, jtag_loc.BAR, *command_w, jtag_loc.BAR, *cpu.debug.command_regs[DMICommand.AccessRegister].fields.values(), jtag_loc.BAR, *dmstatus_r, jtag_loc.BAR, *dmcontrol_w, cpu.gprf_debug_data, cpu.gprf_debug_addr, cpu.halt, ] return { "sim": sim, "frag": frag, "vcd_traces": vcd_traces, "jtag_fsm": jtag_fsm, }
yield return m, f def unit_testbench(case: ComponentTestbenchCase): if case.component_type == MemoryArbiter: m, f = memory_arbiter_tb() elif case.component_type == GPIO_Wishbone: m, f = gpio_tb() else: print(f"===== Skipping not covered type {case.component_type}") return sim = Simulator(m) sim.add_clock(1e-6) sim.add_sync_process(f) with sim.write_vcd(f"{case.name}.vcd"): sim.run() # returns ELF path def compile_sw_project(proj_name : str) -> Path: sw_dir = Path(__file__).parent.parent.parent / "sw" proj_dir = sw_dir / proj_name if not proj_dir.exists() or not proj_dir.is_dir(): raise ValueError(f"Compilation failed: Directory {proj_dir} does not exists!") from mtkcpu.utils.linker import write_linker_script write_linker_script(sw_dir / "common" / "linker.ld", CODE_START_ADDR) process = subprocess.Popen(f"make -B", cwd=proj_dir, shell=True)
def test_with_samplerate(samplerate: int=48000): """run adat signal simulation with the given samplerate""" # 24 bit plus the 6 nibble separator bits for eight channel # then 1 separator, 10 sync bits (zero), 1 separator and 4 user bits clk_freq = 100e6 dut = ADATReceiverTester(clk_freq) adat_freq = NRZIDecoder.adat_freq(samplerate) clockratio = clk_freq / adat_freq sim = Simulator(dut) sim.add_clock(1.0/clk_freq, domain="sync") sim.add_clock(1.0/adat_freq, domain="adat") sixteen_adat_frames = sixteen_frames_with_channel_num_msb_and_sample_num() testdata = \ one_empty_adat_frame() + \ sixteen_adat_frames[0:256] + \ [0] * 64 + \ sixteen_adat_frames[256:] testdata_nrzi = encode_nrzi(testdata) print(f"FPGA clock freq: {clk_freq}") print(f"ADAT clock freq: {adat_freq}") print(f"FPGA/ADAT freq: {clockratio}") no_cycles = len(testdata_nrzi) + 500 # Send the adat stream def adat_process(): for bit in testdata_nrzi: # [224:512 * 2]: yield dut.adat_in.eq(bit) yield Tick("adat") # Process the adat stream and validate output def sync_process(): # Obtain the output data out_data = [[0 for x in range(9)] for y in range(16)] #store userdata in the 9th column sample = 0 for _ in range(int(clockratio) * no_cycles): yield Tick("sync") if (yield dut.output_enable == 1): channel = yield dut.addr_out out_data[sample][channel] = yield dut.sample_out if (channel == 7): out_data[sample][8] = yield dut.user_data_out sample += 1 #print(out_data) # # The receiver needs 2 sync pads before it starts outputting data: # * The first sync pad is needed for the nrzidecoder to sync # * The second sync pad is needed for the receiver to sync # Therefore each time after the connection was lost the first frame will be lost while syncing. # In our testdata we loose the initial one_empty_adat_frame and the second sample (#1, count starts with 0) # sampleno = 0 for i in range(16): if (sampleno == 1): #skip the first frame while the receiver syncs after an interruption sampleno += 1 elif (sampleno == 16): #ensure the data ended as expected assert out_data[i] == [0, 0, 0, 0, 0, 0, 0, 0, 0], "Sample {} was: {}".format(sampleno, print_frame(out_data[sampleno])) else: assert out_data[i] == [((0 << 20) | sampleno), ((1 << 20) | sampleno), ((2 << 20) | sampleno), ((3 << 20) | sampleno), ((4 << 20) | sampleno), ((5 << 20) | sampleno), ((6 << 20) | sampleno), ((7 << 20) | sampleno), 0b0101]\ , "Sample #{} was: {}".format(sampleno, print_frame(out_data[sampleno])) sampleno += 1 print("Success!") sim.add_sync_process(sync_process, domain="sync") sim.add_sync_process(adat_process, domain="adat") with sim.write_vcd(f'receiver-smoke-test-{str(samplerate)}.vcd'): sim.run()
Mux(val <= end_val, 0b1111, 0b0000)) m.d.rx += val.eq(val + 1) m.d.rx += buffer.tlp_source.ready.eq(1) def store_tlp(start, end, id): return [ val.eq(start), end_val.eq(end), buffer.store_tlp.eq(1), buffer.store_tlp_id.eq(id) ] sim = Simulator(m) sim.add_clock(1E-9, domain="rx") def process(): sm1 = 1 for i in range(1000): if i == 0: for statement in store_tlp(5, 8, 0xAB): yield statement print("Store 1") # This shouldn't overwrite an existing TLP if i == 50: for statement in store_tlp(9, 17, 0xAB): yield statement print("Store 2")
yield Tick("usb") # send USB MIDI sysex yield from sysex(0x04, 0xf0, 0x0a, 0x0b, 0x07, 0x0c, 0x0d, 0xf7) for _ in range(40): yield Tick("usb") yield dut.midi_stream.valid.eq(1) yield from midi_message(0x93, 60, 0x7f, set_valid=False) yield Tick("usb") yield from midi_message(0x93, 61, 0x7f, set_valid=False) yield dut.midi_stream.valid.eq(0) for _ in range(128): yield Tick("usb") def jt51_process(): yield Tick("jt51") yield Tick("jt51") yield Tick("jt51") yield Tick("jt51") yield dut.jt51_stream.ready.eq(1) for _ in range(50): yield Tick("jt51") sim = Simulator(dut) sim.add_clock(1.0/60e6, domain="usb") sim.add_clock(1.0/3e6, domain="jt51") sim.add_sync_process(usb_process, domain="usb") sim.add_sync_process(jt51_process, domain="jt51") with sim.write_vcd(f'midicontroller.vcd'): sim.run()
# Disabled counter should not overflow. yield dut.en.eq(0) for _ in range(30): yield assert not (yield dut.ovf) # Once enabled, the counter should overflow in 25 cycles. yield dut.en.eq(1) for _ in range(25): yield assert not (yield dut.ovf) yield assert (yield dut.ovf) # The overflow should clear in one cycle. yield assert not (yield dut.ovf) sim = Simulator(dut) sim.add_clock(1e-6) # 1 MHz sim.add_sync_process(bench) with sim.write_vcd("up_counter.vcd"): sim.run() # --- CONVERT --- from amaranth.back import verilog top = UpCounter(25) with open("up_counter.v", "w") as f: f.write(verilog.convert(top, ports=[top.en, top.ovf]))
if __name__ == "__main__": test_bytes = [0x1, 0x23] + [i for i in range(256)] * 4 test_bytes = test_bytes m = Module() in_symbol = Signal(8) reset_crc = Signal(reset=1) m.submodules.crc = crc = LCRC(in_symbol, reset_crc) sim = Simulator(m) sim.add_clock(1, domain="sync") def process(): for i in range(len(test_bytes) + 2): crc_value = ((yield crc.output)) yield in_symbol.eq(test_bytes[min(i, len(test_bytes) - 1)]) yield reset_crc.eq(0) print(i, hex(crc_value)) yield if i == len(test_bytes) + 1: assert crc_value == lcrc(test_bytes) print("Test passed! LCRC:", hex(crc_value)) sim.add_sync_process(process, domain="sync")
class LunaGatewareTestCase(unittest.TestCase): domain = 'sync' # Convenience property: if set, instantiate_dut will automatically create # the relevant fragment with FRAGMENT_ARGUMENTS. FRAGMENT_UNDER_TEST = None FRAGMENT_ARGUMENTS = {} # Convenience properties: if not None, a clock with the relevant frequency # will automatically be added. FAST_CLOCK_FREQUENCY = None SYNC_CLOCK_FREQUENCY = 120e6 USB_CLOCK_FREQUENCY = None SS_CLOCK_FREQUENCY = None def instantiate_dut(self): """ Basic-most function to instantiate a device-under-test. By default, instantiates FRAGMENT_UNDER_TEST. """ return self.FRAGMENT_UNDER_TEST(**self.FRAGMENT_ARGUMENTS) def get_vcd_name(self): """ Return the name to use for any VCDs generated by this class. """ return "test_{}".format(self.__class__.__name__) def setUp(self): self.dut = self.instantiate_dut() self.sim = Simulator(self.dut) if self.USB_CLOCK_FREQUENCY: self.sim.add_clock(1 / self.USB_CLOCK_FREQUENCY, domain="usb") if self.SYNC_CLOCK_FREQUENCY: self.sim.add_clock(1 / self.SYNC_CLOCK_FREQUENCY, domain="sync") if self.FAST_CLOCK_FREQUENCY: self.sim.add_clock(1 / self.FAST_CLOCK_FREQUENCY, domain="fast") if self.SS_CLOCK_FREQUENCY: self.sim.add_clock(1 / self.SS_CLOCK_FREQUENCY, domain="ss") def initialize_signals(self): """ Provide an opportunity for the test apparatus to initialize siganls. """ yield Signal() def traces_of_interest(self): """ Returns an interable of traces to include in any generated output. """ return () def simulate(self, *, vcd_suffix=None): """ Runs our core simulation. """ # If we're generating VCDs, run the test under a VCD writer. if os.getenv('GENERATE_VCDS', default=False): # Figure out the name of our VCD files... vcd_name = self.get_vcd_name() if vcd_suffix: vcd_name = "{}_{}".format(vcd_name, vcd_suffix) # ... and run the simulation while writing them. traces = self.traces_of_interest() with self.sim.write_vcd(vcd_name + ".vcd", vcd_name + ".gtkw", traces=traces): self.sim.run() else: self.sim.run() @staticmethod def pulse(signal, *, step_after=True): """ Helper method that asserts a signal for a cycle. """ yield signal.eq(1) yield yield signal.eq(0) if step_after: yield @staticmethod def advance_cycles(cycles): """ Helper methods that waits for a given number of cycles. """ for _ in range(cycles): yield @staticmethod def wait_until(strobe, *, timeout=None): """ Helper method that advances time until a strobe signal becomes true. """ cycles_passed = 0 while not (yield strobe): yield cycles_passed += 1 if timeout and cycles_passed > timeout: raise RuntimeError( f"Timeout waiting for '{strobe.name}' to go high!") def _ensure_clocks_present(self): """ Function that validates that a clock is present for our simulation domain. """ frequencies = { 'sync': self.SYNC_CLOCK_FREQUENCY, 'usb': self.USB_CLOCK_FREQUENCY, 'fast': self.FAST_CLOCK_FREQUENCY, 'ss': self.SS_CLOCK_FREQUENCY } self.assertIsNotNone( frequencies[self.domain], f"no frequency provied for `{self.domain}`-domain clock!") def wait(self, time): """ Helper method that waits for a given number of seconds in a *_test_case. """ # Figure out the period of the clock we want to work with... if self.domain == 'sync': period = 1 / self.SYNC_CLOCK_FREQUENCY elif self.domain == 'usb': period = 1 / self.USB_CLOCK_FREQUENCY elif self.domain == 'fast': period = 1 / self.FAST_CLOCK_FREQUENCY # ... and, accordingly, how many cycles we want to delay. cycles = math.ceil(time / period) print(cycles) # Finally, wait that many cycles. yield from self.advance_cycles(cycles)
def test_with_samplerate(samplerate: int = 48000): clk_freq = 50e6 dut = ADATTransmitter() adat_freq = NRZIDecoder.adat_freq(samplerate) clockratio = clk_freq / adat_freq print(f"FPGA clock freq: {clk_freq}") print(f"ADAT clock freq: {adat_freq}") print(f"FPGA/ADAT freq: {clockratio}") sim = Simulator(dut) sim.add_clock(1.0 / clk_freq, domain="sync") sim.add_clock(1.0 / adat_freq, domain="adat") def write(addr: int, sample: int, last: bool = False, drop_valid: bool = False): while (yield dut.ready_out == 0): yield from wait(1) if last: yield dut.last_in.eq(1) yield dut.addr_in.eq(addr) yield dut.sample_in.eq(sample) yield dut.valid_in.eq(1) yield Tick("sync") if drop_valid: yield dut.valid_in.eq(0) if last: yield dut.last_in.eq(0) def wait(n_cycles: int): for _ in range(int(clockratio) * n_cycles): yield Tick("sync") def sync_process(): yield Tick("sync") yield Tick("sync") yield dut.user_data_in.eq(0xf) for i in range(4): yield from write(i, i, drop_valid=True) for i in range(4): yield from write(4 + i, 0xc + i, i == 3, drop_valid=True) yield dut.user_data_in.eq(0xa) yield Tick("sync") for i in range(8): yield from write(i, (i + 1) << 4, i == 7) yield dut.user_data_in.eq(0xb) yield Tick("sync") for i in range(8): yield from write(i, (i + 1) << 8, i == 7) yield dut.user_data_in.eq(0xc) yield Tick("sync") for i in range(8): yield from write(i, (i + 1) << 12, i == 7) yield dut.user_data_in.eq(0xd) yield Tick("sync") for i in range(8): yield from write(i, (i + 1) << 16, i == 7) yield dut.user_data_in.eq(0xe) yield Tick("sync") for i in range(8): yield from write(i, (i + 1) << 20, i == 7, drop_valid=True) yield from wait(900) def adat_process(): nrzi = [] i = 0 while i < 1800: yield Tick("adat") out = yield dut.adat_out nrzi.append(out) i += 1 # skip initial zeros nrzi = nrzi[nrzi.index(1):] signal = decode_nrzi(nrzi) decoded = adat_decode(signal) print(decoded) user_bits = [decoded[frame][0] for frame in range(7)] assert user_bits == [0x0, 0xf, 0xa, 0xb, 0xc, 0xd, 0xe], print_assert_failure(user_bits) assert decoded[0][1:] == [0, 0, 0, 0, 0, 0, 0, 0], print_assert_failure(decoded[0][1:]) assert decoded[1][1:] == [0, 1, 2, 3, 0xc, 0xd, 0xe, 0xf], print_assert_failure(decoded[1][1:]) assert decoded[2][1:] == [ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 ], print_assert_failure(decoded[2][1:]) assert decoded[3][1:] == [ 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800 ], print_assert_failure(decoded[3][1:]) assert decoded[4][1:] == [ 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000 ], print_assert_failure(decoded[4][1:]) assert decoded[5][1:] == [ 0x10000, 0x20000, 0x30000, 0x40000, 0x50000, 0x60000, 0x70000, 0x80000 ], print_assert_failure(decoded[5][1:]) sim.add_sync_process(sync_process, domain="sync") sim.add_sync_process(adat_process, domain="adat") with sim.write_vcd(f'transmitter-smoke-test-{str(samplerate)}.vcd'): sim.run()
def test_riscv_dv(cfg: RiscvDvTestConfig): sys.setrecursionlimit(10**6) # otherwise amaranth/sim/_pyrtl.py:441: RecursionError, because of huge memory size max_code_size = cfg.test_elf.stat().st_size program = read_elf(cfg.test_elf) mem_cfg = EBRMemConfig.from_mem_dict( start_addr=cfg.start_addr, num_bytes=max_code_size, simulate=True, mem_dict=MemoryContents(program), ) cpu = MtkCpu( with_debug=False, mem_config=mem_cfg ) sim = Simulator(cpu) sim.add_clock(1e-6) traces = [ cpu.pc, cpu.instr, cpu.rd, cpu.should_write_rd, *cpu.csr_unit.satp.fields.values(), cpu.exception_unit.current_priv_mode, cpu.arbiter.pe.i, cpu.arbiter.pe.o, cpu.arbiter.pe.none, cpu.arbiter.bus_free_to_latch, cpu.arbiter.error_code, cpu.arbiter.addr_translation_en, cpu.arbiter.translation_ack, cpu.arbiter.start_translation, cpu.arbiter.phys_addr, cpu.arbiter.root_ppn, *cpu.arbiter.pte.fields.values(), cpu.arbiter.generic_bus.addr, cpu.arbiter.generic_bus.read_data, cpu.arbiter.vpn, cpu.arbiter.error_code, ] csv_output = tempfile.NamedTemporaryFile( suffix=f".mtkcpu.{cfg.test_name}.csv", dir=Path(__file__).parent.name, delete=False ) fn = riscv_dv_sim_process( cpu=cpu, iss_csv=cfg.iss_csv, compare_every=cfg.compare_every, csv_output=Path(csv_output.name), ) sim.add_sync_process(fn) with sim.write_vcd("cpu.vcd", "cpu.gtkw", traces=traces): sim.run() print("== Waveform dumped to cpu.vcd file")
return m # ------------------------------------------------------------------------------------------------- if __name__ == "__main__": m = Module() m.submodules.pcie = pcie = VirtualPCIeTestbench() dll_status = pcie.phy_d.dll.status sim = Simulator(m) #sim.add_clock(8e-9, domain="tx") #sim.add_clock(8e-9, domain="rx") sim.add_clock(1e-8, domain="sync") # Everything in sync domain def process(): a = 1 last_state = 0 last_retry_buffer_occupation = 0 last_receive_buffer_occupation = 0 last_tx_seq_num = 0 last_rx_seq_num = 0 ack_scheduled_last = 0 for i in range(100 * 1000 * 24): state = State((yield pcie.phy_d.ltssm.debug_state)).name rx_d_data = yield pcie.phy_d.descrambled_lane.rx_symbol tx_d_data = yield pcie.phy_d.descrambled_lane.tx_symbol
def test_with_samplerate(samplerate: int=48000): """run adat signal simulation with the given samplerate""" # 24 bit plus the 6 nibble separator bits for eight channel # then 1 separator, 10 sync bits (zero), 1 separator and 4 user bits clk_freq = 100e6 dut = NRZIDecoderTester(clk_freq) adat_freq = NRZIDecoder.adat_freq(samplerate) clockratio = clk_freq / adat_freq sim = Simulator(dut) sim.add_clock(1.0/clk_freq, domain="sync") sim.add_clock(1.0/adat_freq, domain="adat") print(f"FPGA clock freq: {clk_freq}") print(f"ADAT clock freq: {adat_freq}") print(f"FPGA/ADAT freq: {clockratio}") sixteen_adat_frames = sixteen_frames_with_channel_num_msb_and_sample_num() interrupted_adat_stream = [0] * 64 testdata = one_empty_adat_frame() + \ sixteen_adat_frames[0:256] + \ interrupted_adat_stream + \ sixteen_adat_frames[256:] testdata_nrzi = encode_nrzi(testdata) no_cycles = len(testdata_nrzi) # Send the adat stream def adat_process(): bitcount :int = 0 for bit in testdata_nrzi: #[224:512 * 2]: if (bitcount == 4 * 256 + 64): yield dut.invalid_frame_in.eq(1) yield Tick("adat") yield dut.invalid_frame_in.eq(0) for _ in range(20): yield Tick("adat") else: yield dut.invalid_frame_in.eq(0) yield dut.nrzi_in.eq(bit) yield Tick("adat") bitcount += 1 # Process the adat stream and validate output def sync_process(): # Obtain the output data out_data = [] for _ in range(int(clockratio) * no_cycles): yield Tick("sync") if (yield dut.data_out_en == 1): bit = yield dut.data_out yield out_data.append(bit) # # Validate output # # omit a 1 at the end of the sync pad out_data = out_data[1:] # Whenever the state machine switches from SYNC to DECODE we need to omit the first 11 sync bits validate_output(out_data[:256 - 12], one_empty_adat_frame()[12:256]) out_data = out_data[256-12:] validate_output(out_data[:256], sixteen_adat_frames[:256]) out_data = out_data[256:] # now the adat stream was interrupted, it continues to output zeroes, until it enters the SYNC state validate_output(out_data[:10], interrupted_adat_stream[:10]) out_data = out_data[10:] # followed by 2 well formed adat frames # omit the first 11 sync bits validate_output(out_data[:256 - 12], sixteen_adat_frames[256 + 12:2 * 256]) out_data = out_data[256 - 12:] validate_output(out_data[:256], sixteen_adat_frames[2 * 256:3 * 256]) out_data = out_data[256:] # followed by one invalid frame - the state machine SYNCs again # followed by 13 well-formed frames # omit the first 11 sync bits validate_output(out_data[:256 - 12], sixteen_adat_frames[3 * 256 + 12:4 * 256]) out_data = out_data[256-12:] for i in range(4, 16): validate_output(out_data[:256], sixteen_adat_frames[i * 256:(i + 1) * 256]) out_data = out_data[256:] print("Success!") sim.add_sync_process(sync_process, domain="sync") sim.add_sync_process(adat_process, domain="adat") with sim.write_vcd(f'nrzi-decoder-bench-{str(samplerate)}.vcd'): sim.run()