async def test_regfile_concurrentrw(dut): """Test regfile concurrent read/write to same address""" clkedge = init_posedge_clk(dut.regfile_clk) # Reset dut.regfile_nreset <= 0 await clkedge dut.regfile_nreset <= 1 await clkedge test_addr = 4 test_value = 0xbeefbeef dut.regfile_write_addr1.setimmediatevalue(0) dut.regfile_write_enable1.setimmediatevalue(0) dut.regfile_write_value1.setimmediatevalue(0) dut.regfile_read_addr1.setimmediatevalue(0) dut.regfile_read_addr2.setimmediatevalue(0) await clkedge await Timer(1, 'us') assert_neq(dut.regfile_read_value1, test_value) assert_neq(dut.regfile_read_value2, test_value) dut.regfile_write_addr1.setimmediatevalue(test_addr) dut.regfile_write_value1.setimmediatevalue(test_value) dut.regfile_write_enable1.setimmediatevalue(1) # Read out value as soon as we can dut.regfile_read_addr1.setimmediatevalue(test_addr) dut.regfile_read_addr2.setimmediatevalue(test_addr) await clkedge await Timer(1, 'us') assert_eq(dut.regfile_read_value1, test_value) assert_eq(dut.regfile_read_value2, test_value)
async def test_decoder_assert(dut): """Test decoder assertions against the lab test code""" clkedge = init_posedge_clk(dut.decoder_clk) # Reset and enable dut.decoder_nreset <= 0 await clkedge dut.decoder_nreset <= 1 dut.decoder_enable <= 1 # Read instruction hex with open('cpu/init/code.hex') as code_hex: for inst_hexstr in code_hex.read().splitlines(): dut._log.debug('Testing decoder instruction:', inst_hexstr) instr = int(inst_hexstr, 16) dut.decoder_fetcher_inst.setimmediatevalue(instr) assert dut.decoder_fetcher_inst == instr await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.decoder_ready.value.integer assert dut.decoder_fetcher_inst == instr assert dut.decoder_decoder_inst == instr # Reset dut to initial state dut.decoder_enable.setimmediatevalue(0)
async def test_decoder_stall_for_ldr(dut): """Test decoder's stalling for LDR""" clkedge = init_posedge_clk(dut.decoder_clk) # Need to add PC offset when setting decoder_pc due to pipelining decoder_pc_offset = 8 # Reset and enable dut.decoder_nreset <= 0 await clkedge dut.decoder_nreset <= 1 dut.decoder_enable <= 1 dut.decoder_fetcher_inst <= 0xe79842a9 # ldr r4, [r8, r9, lsr #5] await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.decoder_ready.value.integer assert_eq(dut.decoder_regfile_read_addr1, 8) assert_eq(dut.decoder_regfile_read_addr2, 9) dut.decoder_fetcher_inst <= 0xe1a05004 # mov r5, r4 await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.decoder_ready.value.integer assert_eq(dut.decoder_regfile_read_addr2, 4) # r4 assert dut.decoder_stall_for_ldr.value.integer
async def test_executor_data_single(dut): """Test executor on a single data instruction""" clkedge = init_posedge_clk(dut.executor_clk) # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 # Test result of one data instruction dut.executor_decoder_inst <= int('e0864007', 16) # add r4, r6, r7 Rn_value = 3 # r6 Rd_Rm_value = 5 # r7 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert dut.executor_databranch_Rd_value == Rn_value + Rd_Rm_value
async def test_regfile_pc(dut): """Test regfile PC-specific updates""" clkedge = init_posedge_clk(dut.regfile_clk) # Reset dut.regfile_nreset <= 0 await clkedge dut.regfile_nreset <= 1 await clkedge dut._log.info('Reset complete') # PC should be initialized to zero assert dut.regfile_pc == 0 # Try setting new value dut.regfile_update_pc.setimmediatevalue(1) dut.regfile_new_pc.setimmediatevalue(42) await clkedge # Wait a little bit after clkedge to check immediate values await Timer(1, 'us') assert dut.regfile_pc == 42 # Stop modifying PC so other tests can reset the PC dut.regfile_update_pc.setimmediatevalue(0)
async def test_regfile_write(dut): """Test regfile writes""" clkedge = init_posedge_clk(dut.regfile_clk) expected_regfile = read_regfile_init(mutable=True) async def _check_regfile_expected(): # Set read to some instruction to not trigger PC-specific behavior dut._log.info('Checking regfile values') dut.regfile_read_inst.setimmediatevalue(int('e1540005', 16)) # cmp r4, r5 for addr in range(len(expected_regfile)): dut.regfile_read_addr1.setimmediatevalue(addr) dut.regfile_read_addr2.setimmediatevalue(addr) await clkedge # Wait a little bit after clkedge to check immediate values await Timer(1, 'us') assert dut.regfile_read_value1 == expected_regfile[addr] assert dut.regfile_read_value2 == expected_regfile[addr] async def _write_and_check(new_addr, new_value): """Do a quick check of the value written""" dut._log.info( f"Start write and check for r{new_addr} = {hex(new_value)}") assert expected_regfile[new_addr] != new_value expected_regfile[new_addr] = new_value dut.regfile_write_addr1.setimmediatevalue(new_addr) dut.regfile_write_value1.setimmediatevalue(new_value) dut.regfile_write_enable1.setimmediatevalue(1) # Read out value as soon as we can dut.regfile_read_addr1.setimmediatevalue(new_addr) await clkedge # Wait a little bit after clkedge to check immediate values await Timer(1, 'us') assert_eq(dut.regfile_read_value1, new_value) # Reset dut.regfile_nreset <= 0 await clkedge dut.regfile_nreset <= 1 await clkedge dut._log.info('Reset complete') # Save original files so we can restore them at the end of the test orig_r4 = expected_regfile[4] orig_r5 = expected_regfile[5] await _check_regfile_expected() await _write_and_check(4, 0xdead) await _write_and_check(4, 0xdeadbeef) await _write_and_check(5, 0xbeef) await _check_regfile_expected() await _write_and_check(4, orig_r4) await _write_and_check(5, orig_r5)
async def _setup_data_memory(dut): clkedge = init_posedge_clk(dut.data_memory_clk) # Reset and enable dut.data_memory_nreset <= 0 await clkedge dut.data_memory_nreset <= 1 return clkedge
async def _setup_memaccessor(dut): clkedge = init_posedge_clk(dut.memaccessor_clk) # Reset and enable dut.memaccessor_nreset <= 0 await clkedge dut.memaccessor_nreset <= 1 return clkedge
async def test_fetcher_disable(dut): """Test if fetcher persists outputs when disabled""" clkedge = init_posedge_clk(dut.fetcher_clk) code_memory = read_code_memory() # Need to add PC offset when setting fetcher_pc due to pipelining fetcher_pc_offset = 8 # Reset and enable dut.fetcher_nreset <= 0 await clkedge dut.fetcher_nreset <= 1 dut.fetcher_enable <= 1 initial_inputs = { dut.fetcher_pc: 64, } expected_outputs = { dut.fetcher_fetcher_inst: code_memory[initial_inputs[dut.fetcher_pc] >> 2], } def _check_expected(): for signal, value in expected_outputs.items(): try: assert_eq(signal, value) except AssertionError as exc: raise AssertionError( 'Failed expected value on signal {} with error: {}'.format( signal._name, str(exc))) # Set initial inputs for signal, value in initial_inputs.items(): signal <= value await clkedge await Timer(1, 'us') assert dut.fetcher_ready.value.integer # Check expected_outputs _check_expected() # Disable module dut.fetcher_enable <= 0 # Scramble inputs for signal in initial_inputs: signal <= 1 await clkedge await Timer(1, 'us') assert not dut.fetcher_ready.value.integer # Check expected outputs _check_expected()
async def _setup_regfilewriter(dut): clkedge = init_posedge_clk(dut.regfilewriter_clk) # Reset and enable dut.regfilewriter_nreset <= 0 await clkedge dut.regfilewriter_nreset <= 1 dut.regfilewriter_enable <= 1 # Set default pc value current_pc = 20 dut.regfilewriter_pc <= current_pc return clkedge, current_pc
async def test_executor_data_conditional(dut): """Test executor on conditional data instructions""" clkedge = init_posedge_clk(dut.executor_clk) # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 # Set CPSR dut.executor_decoder_inst <= int('e1540005', 16) # cmp r4, r5 Rn_value = 65535 # r4 Rd_Rm_value = 65535 # r5 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert not dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer # Test non-execution dut.executor_decoder_inst <= int('13a0e000', 16) # movne lr, #0 await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert not dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer # Test execution dut.executor_decoder_inst <= int('01a0f00e', 16) # moveq pc, lr Rd_Rm_value = 42 # r5 dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert dut.executor_databranch_Rd_value == Rd_Rm_value assert dut.executor_flush_for_pc.value.integer # Reset dut to initial state dut.executor_enable.setimmediatevalue(0)
async def test_executor_memory(dut): """Test executor on memory instructions""" clkedge = init_posedge_clk(dut.executor_clk) # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 dut._log.info("Test LDR") dut.executor_decoder_inst <= int('e79842a9', 16) # ldr r4, [r8, r9, lsr #5] Rn_value = 4 # r8 Rd_Rm_value = 0b1100000 # r9, final value after LSR #5 should be 3 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert not dut.executor_mem_write_enable.value.integer assert dut.executor_mem_read_addr == Rn_value+(Rd_Rm_value>>5) dut._log.info("Test STR") dut.executor_decoder_inst <= int('e5885000', 16) # str r5, [r8] Rd_Rm_value = 0xdeadbeef # r5 Rn_value = 1 # r8 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') # STR should not update Rd assert not dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert dut.executor_mem_write_enable.value.integer assert dut.executor_mem_write_addr == Rn_value assert dut.executor_mem_write_value == Rd_Rm_value # Reset dut to initial state dut.executor_enable.setimmediatevalue(0)
async def test_executor_branch(dut): """Test executor on branch instructions""" clkedge = init_posedge_clk(dut.executor_clk) # Need to add PC offset when setting executor_pc due to pipelining executor_pc_offset = 8 # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 # Test regular branch dut.executor_decoder_inst <= int('eafffffa', 16) # b 0x68 (relative to 0x78) pc_init = 20 dut.executor_pc <= pc_init + executor_pc_offset await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert not dut.executor_update_Rd.value.integer assert dut.executor_flush_for_pc.value.integer assert dut.executor_update_pc.value.integer assert_eq(dut.executor_new_pc, pc_init + (0x68 - 0x78)) # Test branch with link dut.executor_decoder_inst <= int('ebfffffb', 16) # bl 0x68 (relative to 0x74) pc_init = 16 dut.executor_pc <= pc_init + executor_pc_offset await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert dut.executor_flush_for_pc.value.integer assert dut.executor_databranch_Rd_value == pc_init + 4 assert dut.executor_update_pc.value.integer assert_eq(dut.executor_new_pc, pc_init + (0x68 - 0x74)) # Reset dut to initial state dut.executor_enable.setimmediatevalue(0)
async def test_regfile_read(dut): """Test regfile reads""" clkedge = init_posedge_clk(dut.regfile_clk) regfile_init = read_regfile_init() # Reset dut.regfile_nreset <= 0 await clkedge dut.regfile_nreset <= 1 await clkedge dut._log.debug('Reset complete') # Test reads # NOTE: All regfile reads are clocked, so we set immediates here and then # wait for clkedge dut.regfile_read_inst.setimmediatevalue(int('e1540005', 16)) # cmp r4, r5 dut.regfile_read_addr1.setimmediatevalue(4) # r4 dut.regfile_read_addr2.setimmediatevalue(5) # r5 await clkedge # Wait a little bit after clkedge to check immediate values await Timer(1, 'us') assert dut.regfile_read_value1 == regfile_init[4] assert dut.regfile_read_value2 == regfile_init[5] # Test new instruction right away dut.regfile_read_inst.setimmediatevalue(int('e08fe004', 16)) # add lr, pc, r4 dut.regfile_read_addr1.setimmediatevalue(15) # pc dut.regfile_read_addr2.setimmediatevalue(4) # r4 await clkedge # Wait a little bit after clkedge to check immediate values await Timer(1, 'us') # PC should be read directly out, which is zero assert dut.regfile_read_value1 == 0 assert dut.regfile_read_value2 == regfile_init[4]
async def test_cpu(dut): """Run cpu normally and process debug port outputs""" clkedge = init_posedge_clk(dut.cpu_clk) # Reset CPU dut.cpu_nreset <= 0 await clkedge dut.cpu_nreset <= 1 await clkedge dut._log.debug('Reset complete') with open('cpu/init/code.hex') as code_file: num_instructions = len(code_file.read().splitlines()) print("===========BEGIN PARSED DEBUG PORT OUTPUT===========") for cycle_count in range(num_instructions + PIPELINE_PADDING): dut._log.debug(f'Running CPU cycle {cycle_count}') debug_port_bytes = dut.cpu_debug_port_vector.value.integer.to_bytes( DEBUG_BYTES, 'big') parse_cycle_output(cycle_count, debug_port_bytes) await clkedge print("===========END PARSED DEBUG PORT OUTPUT===========")
async def test_decoder_disable(dut): """Test if decoder persists outputs when disabled""" clkedge = init_posedge_clk(dut.decoder_clk) # Need to add PC offset when setting decoder_pc due to pipelining decoder_pc_offset = 8 # Reset and enable dut.decoder_nreset <= 0 await clkedge dut.decoder_nreset <= 1 dut.decoder_enable <= 1 initial_inputs = { dut.decoder_fetcher_inst: 0xe79842a9, # ldr r4, [r8, r9, lsr #5] dut.decoder_regfile_read_value1: 0xdeadbeef, dut.decoder_regfile_read_value2: 0xbeefdead, } # Inputs that need to be scrambled later since they're updated after ready # is asserted delay_scramble = { dut.decoder_regfile_read_value1, dut.decoder_regfile_read_value2, } expected_outputs = { dut.decoder_decoder_inst: initial_inputs[dut.decoder_fetcher_inst], dut.decoder_stall_for_ldr: 0, dut.decoder_regfile_read_inst: initial_inputs[dut.decoder_fetcher_inst], dut.decoder_regfile_read_addr1: 8, # r8 dut.decoder_regfile_read_addr2: 9, # r9 dut.decoder_Rn_value: initial_inputs[dut.decoder_regfile_read_value1], dut.decoder_Rd_Rm_value: initial_inputs[dut.decoder_regfile_read_value2], } def _check_expected(): for signal, value in expected_outputs.items(): try: assert_eq(signal, value) except AssertionError as exc: raise AssertionError( 'Failed expected value on signal {} with error: {}'.format( signal._name, str(exc))) # Set initial inputs for signal, value in initial_inputs.items(): signal <= value await clkedge await Timer(1, 'us') assert dut.decoder_ready.value.integer # Check expected_outputs _check_expected() # Disable module dut.decoder_enable <= 0 # Scramble inputs for signal in initial_inputs: if signal not in delay_scramble: signal <= 1 # set to non-zero to not conflate with Verilator's default value await clkedge await Timer(1, 'us') assert not dut.decoder_ready.value.integer # Check expected outputs _check_expected() # Scramble inputs that need a delay to take effect for signal in delay_scramble: signal <= 1 await clkedge await Timer(1, 'us') assert not dut.decoder_ready.value.integer # Check expected outputs _check_expected()
async def test_executor_disable(dut): """Test if executor persists outputs when disabled""" clkedge = init_posedge_clk(dut.executor_clk) # Need to add PC offset when setting executor_pc due to pipelining executor_pc_offset = 8 # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 async def _test_disable(initial_inputs, expected_outputs, delay_scramble=tuple()): def _check_expected(): for signal, value in expected_outputs.items(): try: assert_eq(signal, value) except AssertionError as exc: raise AssertionError( 'Failed expected value on signal {} with error: {}'.format( signal._name, str(exc) ) ) # Enable module dut.executor_enable <= 1 # Set initial inputs for signal, value in initial_inputs.items(): signal <= value await clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer # Check expected_outputs _check_expected() # Disable module dut.executor_enable <= 0 # Scramble inputs for signal in initial_inputs: if signal not in delay_scramble: signal <= 1 # set to non-zero to not conflate with Verilator's default value await clkedge await Timer(1, 'us') assert not dut.executor_ready.value.integer # Check expected outputs _check_expected() if delay_scramble: # Scramble inputs that need a delay to take effect for signal in delay_scramble: signal <= 1 await clkedge await Timer(1, 'us') assert not dut.executor_ready.value.integer # Check expected outputs _check_expected() Rn_value = 4 # r8 Rd_Rm_value = 0b1100000 # r9, final value after LSR #5 should be 3 initial_inputs = { dut.executor_pc: 64, dut.executor_decoder_inst: 0xe79842a9, # ldr r4, [r8, r9, lsr #5] dut.executor_decoder_Rn_value: Rn_value, dut.executor_decoder_Rd_Rm_value: Rd_Rm_value, dut.executor_memaccessor_fwd_has_Rd: False, # Should be unused values dut.executor_memaccessor_fwd_Rd_addr: 5, dut.executor_memaccessor_fwd_Rd_value: 0xdedebeef, } # Inputs that need to be scrambled later since they're updated after ready # is asserted delay_scramble = set() expected_outputs = { dut.executor_cpsr: 0b0000, dut.executor_condition_passes: True, dut.executor_executor_inst: initial_inputs[dut.executor_decoder_inst], dut.executor_update_pc: False, dut.executor_update_Rd: True, dut.executor_flush_for_pc: False, dut.executor_mem_read_addr: Rn_value+(Rd_Rm_value>>5), dut.executor_mem_write_enable: False, } await _test_disable( initial_inputs, expected_outputs, delay_scramble=delay_scramble) Rd_Rm_value_unused = 0xdeadbeef # r4, should not be used in STR below Rd_Rm_value = 0xbabafafa # r4 initial_inputs = { dut.executor_pc: 64, dut.executor_decoder_inst: 0xe1a05004, # mov r5, r4 dut.executor_decoder_Rn_value: 42, # unused dut.executor_decoder_Rd_Rm_value: Rd_Rm_value_unused, dut.executor_memaccessor_fwd_has_Rd: True, dut.executor_memaccessor_fwd_Rd_addr: 4, # r4 dut.executor_memaccessor_fwd_Rd_value: Rd_Rm_value, } delay_scramble = set() expected_outputs = { dut.executor_cpsr: 0b0000, dut.executor_condition_passes: True, dut.executor_executor_inst: initial_inputs[dut.executor_decoder_inst], dut.executor_update_pc: False, dut.executor_update_Rd: True, dut.executor_databranch_Rd_value: Rd_Rm_value, dut.executor_flush_for_pc: False, dut.executor_mem_write_enable: False, } await _test_disable( initial_inputs, expected_outputs, delay_scramble=delay_scramble) # Test branch with link # Need to add PC offset when setting executor_pc due to pipelining executor_pc_offset = 8 pc_init = 16 initial_inputs = { dut.executor_pc: pc_init + executor_pc_offset, dut.executor_decoder_inst: 0xebfffffb, # bl 0x68 (relative to 0x74) # Unused values dut.executor_decoder_Rn_value: 42, dut.executor_decoder_Rd_Rm_value: 0xbeef, dut.executor_memaccessor_fwd_has_Rd: True, dut.executor_memaccessor_fwd_Rd_addr: 4, dut.executor_memaccessor_fwd_Rd_value: 0xdead, } delay_scramble = set() expected_outputs = { dut.executor_cpsr: 0b0000, dut.executor_condition_passes: True, dut.executor_executor_inst: initial_inputs[dut.executor_decoder_inst], dut.executor_update_pc: True, dut.executor_new_pc: pc_init + (0x68 - 0x74), dut.executor_update_Rd: True, dut.executor_databranch_Rd_value: pc_init + 4, dut.executor_flush_for_pc: True, dut.executor_mem_write_enable: False, } await _test_disable( initial_inputs, expected_outputs, delay_scramble=delay_scramble)
async def test_executor_memaccessor_forward(dut): """Test executor's data forwarding from memaccessor""" clkedge = init_posedge_clk(dut.executor_clk) # Need to add PC offset when setting executor_pc due to pipelining executor_pc_offset = 8 # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 # Steps: # 1. Override r4 value in "mov r5, r4" # 2. Use executor forwarding to move r5 value from "mov r5, r4" to "str r5, [r8]" Rd_Rm_value_unused = 0xdeadbeef # r4, should not be used in STR below Rd_Rm_value = 0xbabafafa # r4 dut.executor_decoder_inst <= 0xe1a05004 # mov r5, r4 dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused dut.executor_memaccessor_fwd_has_Rd <= 1 dut.executor_memaccessor_fwd_Rd_addr <= 4 # r4 dut.executor_memaccessor_fwd_Rd_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value) assert not dut.executor_update_pc.value.integer dut.executor_decoder_inst <= 0xe5885000 # str r5, [r8] Rn_value = 1 # r8 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused await clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert not dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert not dut.executor_update_pc.value.integer assert dut.executor_mem_write_enable.value.integer assert_eq(dut.executor_mem_write_value, Rd_Rm_value) # Repeat same as above, but replace STR with "mov r4, r5" dut.executor_decoder_inst <= 0xe1a05004 # mov r5, r4 dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused dut.executor_memaccessor_fwd_has_Rd <= 1 dut.executor_memaccessor_fwd_Rd_addr <= 4 # r4 dut.executor_memaccessor_fwd_Rd_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value) assert not dut.executor_update_pc.value.integer dut.executor_decoder_inst <= 0xe1a04005 # mov r4, r5 dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused await clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert not dut.executor_update_pc.value.integer assert not dut.executor_mem_write_enable.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value)
async def test_executor_executor_forward(dut): """Test executor's data forwarding to itself""" clkedge = init_posedge_clk(dut.executor_clk) # Need to add PC offset when setting executor_pc due to pipelining executor_pc_offset = 8 # Reset and enable dut.executor_nreset <= 0 await clkedge dut.executor_nreset <= 1 dut.executor_enable <= 1 Rd_Rm_value = 0xdeadbeef # r4 # Try mov, then str (memory forwarding) dut.executor_decoder_inst <= 0xe1a05004 # mov r5, r4 dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value) assert not dut.executor_update_pc.value.integer dut.executor_decoder_inst <= 0xe5885000 # str r5, [r8] Rd_Rm_value_unused = 0xbabafafa # r5, should not be used Rn_value = 1 # r8 dut.executor_decoder_Rn_value <= Rn_value dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused await clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert not dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert not dut.executor_update_pc.value.integer assert dut.executor_mem_write_enable.value.integer assert_eq(dut.executor_mem_write_value, Rd_Rm_value) # Try mov r5, r4, then mov r4, r5 (data forwarding) dut.executor_decoder_inst <= 0xe1a05004 # mov r5, r4 dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value) assert not dut.executor_update_pc.value.integer dut.executor_decoder_inst <= 0xe1a04005 # mov r4, r5 Rd_Rm_value_unused = 0xbabafafa # r5, should not be used dut.executor_decoder_Rn_value <= 42 # unused dut.executor_decoder_Rd_Rm_value <= Rd_Rm_value_unused await clkedge await Timer(1, 'us') assert dut.executor_ready.value.integer assert dut.executor_update_Rd.value.integer assert not dut.executor_flush_for_pc.value.integer assert not dut.executor_update_pc.value.integer assert not dut.executor_mem_write_enable.value.integer assert_eq(dut.executor_databranch_Rd_value, Rd_Rm_value)
async def test_decoder_regfile(dut): """Test decoder regfile addresses""" clkedge = init_posedge_clk(dut.decoder_clk) # Reset and enable dut.decoder_nreset <= 0 await clkedge dut.decoder_nreset.setimmediatevalue(1) dut.decoder_enable.setimmediatevalue(1) # Test data inst dut.decoder_fetcher_inst.setimmediatevalue(int('e0864007', 16)) # add r4, r6, r7 # Make immediates take effect await Timer(1, 'us') assert dut.decoder_regfile_read_addr1 == 6 assert dut.decoder_regfile_read_addr2 == 7 # Test memory inst dut.decoder_fetcher_inst.setimmediatevalue(int( 'e79842a9', 16)) # ldr r4, [r8, r9, lsr #5] # Make immediates take effect await Timer(1, 'us') assert dut.decoder_regfile_read_addr1 == 8 assert dut.decoder_regfile_read_addr2 == 9 # Test PC read dut.decoder_fetcher_inst.setimmediatevalue(int('e08fe004', 16)) # add lr, pc, r4 # Make immediates take effect await Timer(1, 'us') assert dut.decoder_regfile_read_addr1 == 15 # pc assert dut.decoder_regfile_read_addr2 == 4 # r4 # Test that PC is not modified since Rn is PC # Need to await clkedge to simulate regfile read await clkedge dut.decoder_regfile_read_value1.setimmediatevalue(42) dut.decoder_regfile_read_value2.setimmediatevalue(24) # Make immediates take effect await Timer(1, 'us') assert dut.decoder_Rn_value == 42 assert dut.decoder_Rd_Rm_value == 24 # Test PC read with fix for operand2 pc value dut.decoder_fetcher_inst.setimmediatevalue(int('e084e00f', 16)) # add lr, r4, pc # Make immediates take effect await Timer(1, 'us') assert dut.decoder_regfile_read_addr1 == 4 # r4 assert dut.decoder_regfile_read_addr2 == 15 # pc # Test that PC is not modified since Rn is PC # Need to await clkedge to simulate regfile read await clkedge dut.decoder_regfile_read_value1.setimmediatevalue(24) dut.decoder_regfile_read_value2.setimmediatevalue(42) # orig_pc + 8 # Make immediates take effect await Timer(1, 'us') assert dut.decoder_Rn_value == 24 assert dut.decoder_Rd_Rm_value == 42 + 4 # orig_pc + 12 = (orig_pc + 8) + 4 # Reset dut to initial state dut.decoder_enable.setimmediatevalue(0)