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)
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)))
async def _test_write(test_addr): test_value = 0xdeadbeef # 1. Read orig values orig_value = read_data_memory_word(test_addr, data_memory) dut.data_memory_read_addr <= test_addr # 2. Write new value to same location dut.data_memory_write_enable <= 1 dut.data_memory_write_addr <= test_addr dut.data_memory_write_value <= test_value await clkedge write_data_memory_word(test_addr, test_value, data_memory) # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') # 3. See if read picks up value on next clock cycle assert_eq(dut.data_memory_read_value, test_value) # 4. Disable write enable, see if read still picks up value (i.e. value actually committed) dut.data_memory_write_enable <= 0 dut.data_memory_write_value <= 0xAABBCCDD await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert_eq(dut.data_memory_read_value, test_value) # 5. Try changing offset of value within word, see if that reflects on reads async def _test_read(read_addr): dut.data_memory_read_addr <= read_addr await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.data_memory_read_value == read_data_memory_word( read_addr, data_memory) await _test_read(test_addr + 1) await _test_read(test_addr - 1) await _test_read(test_addr + 2) await _test_read(test_addr - 2) await _test_read(test_addr + 3) await _test_read(test_addr - 3) # Reset original value dut.data_memory_write_enable <= 1 dut.data_memory_write_addr <= test_addr dut.data_memory_write_value <= orig_value await clkedge write_data_memory_word(test_addr, orig_value, data_memory)
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)
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_memaccessor_read(dut): """Test memaccessor reads""" clkedge = await _setup_memaccessor(dut) data_memory = read_data_memory_init() databranch_Rd_value = 0xbeefdead dut.memaccessor_databranch_Rd_value <= databranch_Rd_value dut.memaccessor_executor_update_pc <= 0 dut.memaccessor_enable <= 1 dut.memaccessor_executor_inst <= int('e5984000', 16) # ldr r4, [r8] Rn_value = 6 # r8 dut.memaccessor_read_addr <= Rn_value dut.memaccessor_executor_update_Rd <= 1 await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.memaccessor_ready.value.integer assert dut.memaccessor_update_Rd.value.integer assert_eq(dut.memaccessor_Rd_value, read_data_memory_word(Rn_value, data_memory)) # Check forwarding values assert dut.memaccessor_fwd_has_Rd.value.integer assert_eq(dut.memaccessor_fwd_Rd_addr, 4) # r4 assert_eq(dut.memaccessor_fwd_Rd_value, read_data_memory_word(Rn_value, data_memory)) dut.memaccessor_enable <= 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_memaccessor_write(dut): """Test memaccessor writes""" clkedge = await _setup_memaccessor(dut) data_memory = read_data_memory_init() databranch_Rd_value = 0xbeefdead dut.memaccessor_databranch_Rd_value <= databranch_Rd_value dut.memaccessor_executor_update_pc <= 0 dut.memaccessor_enable <= 1 dut._log.info("Test STR") dut.memaccessor_executor_inst <= int('e5885000', 16) # str r5, [r8] Rd_Rm_value = 0xdeadbeef # r5 Rn_value = 1 # r8 dut.memaccessor_write_enable <= 1 dut.memaccessor_write_addr <= Rn_value dut.memaccessor_write_value <= Rd_Rm_value dut.memaccessor_executor_update_Rd <= 0 await clkedge write_data_memory_word(Rn_value, Rd_Rm_value, data_memory) # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') dut.memaccessor_write_enable <= 0 # STR should not update Rd assert dut.memaccessor_ready.value.integer assert not dut.memaccessor_update_Rd.value.integer assert not dut.memaccessor_fwd_has_Rd.value.integer dut._log.info("Verify STR with LDR") dut.memaccessor_executor_inst <= int('e5984000', 16) # ldr r4, [r8] Rn_value = 1 # r8 dut.memaccessor_read_addr <= Rn_value dut.memaccessor_executor_update_Rd <= 1 await clkedge # We need to wait a little since the values just became available # at the last clkedge await Timer(1, 'us') assert dut.memaccessor_ready.value.integer assert dut.memaccessor_update_Rd.value.integer assert_eq(dut.memaccessor_Rd_value, Rd_Rm_value) # Check forwarding values assert dut.memaccessor_fwd_has_Rd.value.integer assert_eq(dut.memaccessor_fwd_Rd_addr, 4) # r4 assert_eq(dut.memaccessor_fwd_Rd_value, Rd_Rm_value) dut.memaccessor_enable <= 0
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)