Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
def test_comb_alu():
    alu = ALU()

    def testbench():
        for a in range(20):
            yield alu.a.eq(a)
            for b in range(20):
                yield alu.b.eq(b)

                # In this combinatorial testbench, we use `yield Settle()`
                # to request the simulator advance until all combinatorial
                # statements have been fully resolved.
                yield alu.op.eq(1)
                yield Settle()
                assert (yield alu.y) == a + b

                yield alu.op.eq(0)
                yield Settle()
                assert (yield alu.y) == (a - b) % 512

    sim = Simulator(alu)

    # Instead of `sim.add_sync_process`, which would try to use a clock domain
    # (by default, "sync") and error out saying it doesn't exist, we use
    # `sim.add_process`.
    sim.add_process(testbench)

    sim.run()
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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()
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)
    process.communicate()
    if process.returncode:
        raise ValueError(f"Compilation failed! {proj_name} (inside {proj_dir})")
    elf_path = proj_dir / "build" / f"{proj_name}.elf"
Ejemplo n.º 7
0
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()
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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()
Ejemplo n.º 10
0
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")
Ejemplo n.º 11
0
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()