From a3c5e74a9697b636f7c2f906ae80b296d4ae7b58 Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sat, 20 Jul 2024 23:06:23 +0200 Subject: [PATCH 1/8] debug store byte to address nonzero mod 4 --- Makefile | 2 +- board/breakpoint_example.gdb | 5 +- build_docker_image.sh | 2 +- mtkcpu/tests/test_debug_unit.py | 104 ++++++++++++-- mtkcpu/tests/test_memory.py | 36 +++-- mtkcpu/units/loadstore.py | 231 +++++++++++++------------------- mtkcpu/units/mmio/ebr.py | 1 + 7 files changed, 214 insertions(+), 167 deletions(-) diff --git a/Makefile b/Makefile index 4bd745d..a1cf276 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ build-docker: bash ./build_docker_image.sh DRUN := docker run -v $(MAKEFILE_DIR)/sw:/toolchain/sw $(DOCKER_IMAGE_NAME) -DRUN_IT := docker run -v $(MAKEFILE_DIR)/sw:/toolchain/sw -it $(DOCKER_IMAGE_NAME) +DRUN_IT := docker run -v $(MAKEFILE_DIR)/sw:/toolchain/sw -v /dev:/dev -it $(DOCKER_IMAGE_NAME) unit-test-docker: $(DRUN) poetry run mtkcpu tests cpu diff --git a/board/breakpoint_example.gdb b/board/breakpoint_example.gdb index f39c8cb..0d1618a 100644 --- a/board/breakpoint_example.gdb +++ b/board/breakpoint_example.gdb @@ -4,6 +4,5 @@ set riscv use-compressed-breakpoints no target extended-remote localhost:3333 set mem inaccessible-by-default off set remotetimeout 10 -load -b main -run +# load +# run diff --git a/build_docker_image.sh b/build_docker_image.sh index fd8cdf2..c3ce6b1 100755 --- a/build_docker_image.sh +++ b/build_docker_image.sh @@ -12,5 +12,5 @@ VERSION="1.0.0" IMAGE="mtkcpu:${VERSION}" echo "Building docker image" && \ -docker build --no-cache -t ${IMAGE} . && \ +docker build -t ${IMAGE} . && \ echo "Image was built" diff --git a/mtkcpu/tests/test_debug_unit.py b/mtkcpu/tests/test_debug_unit.py index 872860e..c5e20c5 100755 --- a/mtkcpu/tests/test_debug_unit.py +++ b/mtkcpu/tests/test_debug_unit.py @@ -548,6 +548,83 @@ def main_process(): simulator.run() +@dmi_simulator +def test_xd( + simulator: Simulator, + cpu: MtkCpu, + dmi_monitor: DMI_Monitor, +): + assert PROGBUFSIZE >= 2 + + def main_process(): + yield from few_ticks() + + val = 123 + assert val < 2**11 + + gpr_reg_num = 8 + + for i, ins in enumerate([ + # addi x1, x0, + encode_ins(instructions.Addi(registers.get_register(gpr_reg_num), registers.get_register(0), val)), + encode_ins(instructions.Ebreak()), + ]): + yield from progbuf_write_wait_for_success(dmi_monitor, i, ins) + + yield from trigger_progbuf_exec(dmi_monitor=dmi_monitor) + yield from dmi_op_wait_for_cmderr( + dmi_monitor=dmi_monitor, + expected_cmderr=ABSTRACTCS_Layout.CMDERR.HALT_OR_RESUME, + timeout=1000, + ) + + yield from clear_cmderr_wait_for_success(dmi_monitor=dmi_monitor) + + yield from activate_DM_and_halt_via_dmi(dmi_monitor=dmi_monitor) + halted = yield from cpu_core_is_halted(dmi_monitor=dmi_monitor) + if not halted: + raise ValueError("CPU was not halted after haltreq set!") + + yield from trigger_progbuf_exec(dmi_monitor=dmi_monitor) + yield from dmi_op_wait_for_success(dmi_monitor=dmi_monitor, timeout=100) + + x = yield cpu.regs._array._inner[gpr_reg_num] + + if x != val: + raise ValueError(f"expected x{gpr_reg_num} to contain {hex(val)}, got {hex(x)} instead") + + # make sure that after successfull PROGBUF execution, PC is properly restored. + + def resume_core_via_dmi(): + yield from DMCONTROL_setup_basic_fields(dmi_monitor=dmi_monitor, dmi_op=DMIOp.WRITE) + yield dmi_monitor.cur_DMCONTROL.haltreq.eq(0) + yield dmi_monitor.cur_DMCONTROL.resumereq.eq(1) + yield from dmi_bus_trigger_transaction(dmi_monitor=dmi_monitor) + yield from dmi_op_wait_for_success(dmi_monitor=dmi_monitor) + + yield from resume_core_via_dmi() + + pc = yield cpu.pc + min_incl, max_excl = PROGBUF_MMIO_ADDR, PROGBUF_MMIO_ADDR + 4 * PROGBUFSIZE + progbuf_pc_range = range(min_incl, max_excl, 4) + assert MEM_START_ADDR not in progbuf_pc_range + if pc in progbuf_pc_range: + raise ValueError(f"After successfull PROGBUF execution hart was sent resumereq, but the PC was not updated back! pc={hex(pc)}") + + processes = [ + main_process, + monitor_cpu_and_dm_state(dmi_monitor=dmi_monitor), + bus_capture_write_transactions(cpu=cpu, output_dict=dict()), + monitor_pc_and_main_fsm(dmi_monitor=dmi_monitor, wait_for_first_haltreq=False), + ] + + for p in processes: + simulator.add_sync_process(p) + + simulator.run() + + + @dmi_simulator def test_progbuf_gets_executed( simulator: Simulator, @@ -1012,19 +1089,20 @@ def resume_core_via_dmi(): if __name__ == "__main__": # import pytest # pytest.main(["-x", __file__]) - test_not_supported_command_type_finishes() - test_dmi_try_read_not_implemented_register() - test_dmi_abstract_command_read_write_gpr() - test_core_halt_resume() - test_halt_resume_with_new_dpc() - test_cmderr_clear() - test_progbuf_writes_to_bus() - test_progbuf_gets_executed() - test_progbuf_cmderr_on_runtime_error() - test_access_debug_csr_regs_in_debug_mode() - test_abstracauto_autoexecdata() - test_ebreakm_halt() - test_single_step() + test_xd() + # test_not_supported_command_type_finishes() + # test_dmi_try_read_not_implemented_register() + # test_dmi_abstract_command_read_write_gpr() + # test_core_halt_resume() + # test_halt_resume_with_new_dpc() + # test_cmderr_clear() + # test_progbuf_writes_to_bus() + # test_progbuf_gets_executed() + # test_progbuf_cmderr_on_runtime_error() + # test_access_debug_csr_regs_in_debug_mode() + # test_abstracauto_autoexecdata() + # test_ebreakm_halt() + # test_single_step() logging.critical("ALL TESTS PASSED!") diff --git a/mtkcpu/tests/test_memory.py b/mtkcpu/tests/test_memory.py index 286afa9..d2f3d86 100644 --- a/mtkcpu/tests/test_memory.py +++ b/mtkcpu/tests/test_memory.py @@ -125,18 +125,21 @@ def __call__(self, val=None) -> RegistryContents: reg_init=fill_but_one(), mem_init=MemoryContents(memory={0x20: 0xdeadbeef}), ), - MemTestCase( - name="simple 'sh'", - source_type=MemTestSourceType.TEXT, - source=f""" - .section code - sh x5, 0x10(x{fill_but_one.addr_reg_idx}) - """, - timeout=10, - reg_init=fill_but_one(), - mem_init=MemoryContents(memory={0x10: 5}), - mem_out=MemoryContents(memory={0x10: 5}), - ), + # TODO tu jestem + # MemTestCase( + # name="simple 'sh'", + # source_type=MemTestSourceType.TEXT, + # source=f""" + # .section code + # lui x5, 0xaabbf + # addi x5, x5, 0x01cd + # sh x5, 0x10(x{fill_but_one.addr_reg_idx}) + # """, + # timeout=10, + # reg_init=fill_but_one(), + # mem_init=MemoryContents(memory={0x10: 0}), + # mem_out=MemoryContents(memory={0x10: 0}), + # ), MemTestCase( name="negative 'sh'", source_type=MemTestSourceType.TEXT, @@ -148,17 +151,22 @@ def __call__(self, val=None) -> RegistryContents: reg_init=fill_but_one(-5), mem_out=MemoryContents(memory={0x10: Bits(int=-5, length=16).uint}), ), + + + # TODO tu jestem MemTestCase( name="simple 'sb'", source_type=MemTestSourceType.TEXT, source=f""" .section code - sb x5, 0x10(x{fill_but_one.addr_reg_idx}) + sb x5, 0x11(x{fill_but_one.addr_reg_idx}) """, timeout=10, reg_init=fill_but_one(0xAA), - mem_out=MemoryContents(memory={0x10: 0xAA}), + mem_out=MemoryContents(memory={0x11: 0xAA}), ), + + MemTestCase( name="overwrite 'sb'", source_type=MemTestSourceType.TEXT, diff --git a/mtkcpu/units/loadstore.py b/mtkcpu/units/loadstore.py index e52d616..dab94ed 100644 --- a/mtkcpu/units/loadstore.py +++ b/mtkcpu/units/loadstore.py @@ -338,7 +338,21 @@ def elaborate(self, platform): m.d.comb += self.exception_unit.m_fetch_error.eq(1) with m.Else(): m.d.comb += self.exception_unit.m_load_error.eq(1) - + + with m.FSM(): + with m.State("xd"): + with m.If((gb.addr == 0x800000d6) & gb.store & gb.en): + # verified. proceed to value test + with m.If((gb.mask == 0b001) & (gb.write_data == 0x0)): + m.next = "dx" + with m.State("dx"): + ctr = Signal(20) + if platform: + led_b = platform.request("led_r", 1).o + m.d.sync += ctr.eq(ctr + 1) + with m.If(ctr == 0): + m.d.sync += led_b.eq(~led_b) + with m.If(~pe.none): # transaction request occured for i, priority in enumerate(sorted_ports): @@ -397,60 +411,61 @@ def error(code: Issue): sv32_i = Signal(reset=1) root_ppn = self.root_ppn = Signal(22) - with m.FSM(): - with m.State("IDLE"): - with m.If(start_translation): - sync += sv32_i.eq(1) - if self.with_addr_translation: + if self.with_addr_translation: + with m.FSM(): + with m.State("IDLE"): + with m.If(start_translation): + sync += sv32_i.eq(1) sync += root_ppn.eq(self.csr_unit.satp.as_view().ppn) - m.next = "TRANSLATE" - with m.State("TRANSLATE"): - vpn = self.vpn = Signal(10) - comb += vpn.eq(Mux( - sv32_i, - vaddr.vpn1, - vaddr.vpn0, - )) - comb += [ - gb.en.eq(1), - gb.addr.eq(Cat(Const(0, 2), vpn, root_ppn)), - gb.store.eq(0), - gb.mask.eq(0b1111), # TODO use -1 - ] - with m.If(gb.ack): - sync += pte.eq(gb.read_data) - m.next = "PROCESS_PTE" - with m.State("PROCESS_PTE"): - with m.If(~pte.v): - error(Issue.PAGE_INVALID) - with m.If(pte.w & ~pte.r): - error(Issue.WRITABLE_NOT_READABLE) - - is_leaf = lambda pte: pte.r | pte.x - with m.If(is_leaf(pte)): - with m.If(~pte.u & (self.exception_unit.current_priv_mode == PrivModeBits.USER)): - error(Issue.LACK_PERMISSIONS) - with m.If(~pte.a | (req_is_write & ~pte.d)): - error(Issue.FIRST_ACCESS) - with m.If(sv32_i.bool() & pte.ppn0.bool()): - error(Issue.MISALIGNED_SUPERPAGE) - # phys_addr could be 34 bits long, but our interconnect is 32-bit long. - # below statement cuts lowest two bits of r-value. - sync += phys_addr.eq(Cat(vaddr.page_offset, pte.ppn0, pte.ppn1)) - with m.Else(): # not a leaf - with m.If(sv32_i == 0): - error(Issue.LEAF_IS_NO_LEAF) - sync += root_ppn.eq(Cat(pte.ppn0, pte.ppn1)) # pte a is pointer to the next level - m.next = "NEXT" - with m.State("NEXT"): - # Note that we cannot check 'sv32_i == 0', becuase superpages can be present. - with m.If(is_leaf(pte)): - sync += sv32_i.eq(1) - comb += translation_ack.eq(1) # notify that 'phys_addr' signal is set - m.next = "IDLE" - with m.Else(): - sync += sv32_i.eq(0) - m.next = "TRANSLATE" + m.next = "TRANSLATE" + with m.State("TRANSLATE"): + vpn = self.vpn = Signal(10) + comb += vpn.eq(Mux( + sv32_i, + vaddr.vpn1, + vaddr.vpn0, + )) + comb += [ + gb.en.eq(1), + gb.addr.eq(Cat(Const(0, 2), vpn, root_ppn)), + gb.store.eq(0), + gb.mask.eq(0b1111), # TODO use -1 + ] + with m.If(gb.ack): + sync += pte.eq(gb.read_data) + m.next = "PROCESS_PTE" + with m.State("PROCESS_PTE"): + with m.If(~pte.v): + error(Issue.PAGE_INVALID) + with m.If(pte.w & ~pte.r): + error(Issue.WRITABLE_NOT_READABLE) + + is_leaf = lambda pte: pte.r | pte.x + with m.If(is_leaf(pte)): + with m.If(~pte.u & (self.exception_unit.current_priv_mode == PrivModeBits.USER)): + error(Issue.LACK_PERMISSIONS) + with m.If(~pte.a | (req_is_write & ~pte.d)): + error(Issue.FIRST_ACCESS) + with m.If(sv32_i.bool() & pte.ppn0.bool()): + error(Issue.MISALIGNED_SUPERPAGE) + # phys_addr could be 34 bits long, but our interconnect is 32-bit long. + # below statement cuts lowest two bits of r-value. + sync += phys_addr.eq(Cat(vaddr.page_offset, pte.ppn0, pte.ppn1)) + with m.Else(): # not a leaf + with m.If(sv32_i == 0): + error(Issue.LEAF_IS_NO_LEAF) + sync += root_ppn.eq(Cat(pte.ppn0, pte.ppn1)) # pte a is pointer to the next level + m.next = "NEXT" + with m.State("NEXT"): + # Note that we cannot check 'sv32_i == 0', becuase superpages can be present. + with m.If(is_leaf(pte)): + sync += sv32_i.eq(1) + comb += translation_ack.eq(1) # notify that 'phys_addr' signal is set + m.next = "IDLE" + with m.Else(): + sync += sv32_i.eq(0) + m.next = "TRANSLATE" + return m def port(self, priority): @@ -489,39 +504,6 @@ def match_loadstore_unit(op, f3, f7): op, f3, f7 ) - -class Selector(Elaboratable): - def __init__(self): - self.mask = Signal(4, name="SEL_mask") - self.funct3 = Signal(Funct3) - self.store = Signal() - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - - with m.Switch(self.funct3): - with m.Case(Funct3.W): - comb += self.mask.eq(0b1111) - with m.Case(Funct3.H): - comb += self.mask.eq(0b0011) - with m.Case(Funct3.B): - comb += self.mask.eq(0b0001) - with m.Case(Funct3.HU): - comb += self.mask.eq(0b0011) - with m.Case(Funct3.BU): - comb += self.mask.eq(0b0001) - - return m - - -def prefix_all_signals(obj, prefix): - for attr_name in dir(obj): - sig = getattr(obj, attr_name) - if type(sig) == Signal: - sig.name = prefix + sig.name - - class GenericInterfaceToWishboneMasterBridge(Elaboratable): def __init__(self, wb_bus : WishboneBusRecord, generic_bus : LoadStoreInterface): super().__init__() @@ -575,54 +557,34 @@ def __init__(self, mem_port : LoadStoreInterface): def elaborate(self, platform): m = Module() + comb = m.d.comb - sync = m.d.sync loadstore = self.loadstore - store = self.store + addr = Signal(32) - - comb += [ - addr.eq(self.offset + self.src1), - ] - - sel = m.submodules.sel = Selector() - - # sel.mask will be calculated. - comb += [ - sel.funct3.eq(self.funct3), - sel.store.eq(store), - ] - word = Signal(signed(32)) half_word = Signal(signed(16)) byte = Signal(8) - write_data = Signal(32) - signed_write_data = Signal(signed(32)) - load_res = Signal(signed(32)) - addr_lsb = Signal(2) - m.d.comb += addr_lsb.eq(addr[:2]) # XXX + + comb += [ + addr.eq(self.offset + self.src1), + addr_lsb.eq(addr[:2]), + ] # allow naturally aligned addresses # TODO trap on wrong address - with m.If(store): - data = self.src2 - comb += [ - word.eq(data), - half_word.eq(data.word_select(addr_lsb[1], 16)), - byte.eq(data.word_select(addr_lsb, 8)), - ] - with m.Else(): - data = loadstore.read_data - comb += [ - word.eq(data), - half_word.eq(data.word_select(addr_lsb[1], 16)), - byte.eq(data.word_select(addr_lsb, 8)) - ] + data = Mux(store, self.src2, loadstore.read_data) + comb += [ + word.eq(data), + half_word.eq(data.word_select(addr_lsb[1], 16)), + byte.eq(data.word_select(addr_lsb, 8)), + ] + # TODO choice expression (amaranth-0.6) with m.If(~store): with m.Switch(self.funct3): with m.Case(Funct3.W): @@ -638,30 +600,29 @@ def elaborate(self, platform): with m.If(store): with m.Switch(self.funct3): + mask = Signal(4) with m.Case(Funct3.W): - comb += (write_data.eq(word),) - with m.Case(Funct3.H): comb += [ - signed_write_data.eq(half_word), - write_data.eq(signed_write_data), + write_data.eq(word), + mask.eq(0b1111), ] - with m.Case(Funct3.B): + with m.Case(Funct3.H, Funct3.HU): comb += [ - signed_write_data.eq(byte), - write_data.eq(signed_write_data), + write_data.eq(half_word), + mask.eq(0b11 >> addr_lsb[1]), + ] + with m.Case(Funct3.B, Funct3.BU): + comb += [ + write_data.eq(byte), + mask.eq(0b1 >> addr_lsb), ] - with m.Case(Funct3.HU): - comb += (write_data.eq(half_word),) - with m.Case(Funct3.BU): - comb += (write_data.eq(byte),) - with m.If(self.en): comb += [ loadstore.en.eq(1), loadstore.store.eq(store), - loadstore.addr.eq(addr), - loadstore.mask.eq(sel.mask), + loadstore.addr.eq(addr & ~Const(0b11, 32)), + loadstore.mask.eq(mask), loadstore.write_data.eq(write_data), ] with m.If(loadstore.ack): diff --git a/mtkcpu/units/mmio/ebr.py b/mtkcpu/units/mmio/ebr.py index 6fc5d4c..fcaa8b2 100644 --- a/mtkcpu/units/mmio/ebr.py +++ b/mtkcpu/units/mmio/ebr.py @@ -45,6 +45,7 @@ def handle_transaction(self, wb_slv_module): ws_bytes = self.mem_config.word_size assert log2(ws_bytes).is_integer() ws_bit_shift = Const(int(log2(ws_bytes))) + # raise ValueError(ws_bit_shift) real_addr = Signal(32) wb_comb += real_addr.eq(addr >> ws_bit_shift) From 5ad8c012b84c3cf1bd8d71e638cfe88b39c09d78 Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sun, 21 Jul 2024 23:24:32 +0200 Subject: [PATCH 2/8] cont - allt tests passing but mmu addr translation --- mtkcpu/cpu/cpu.py | 2 +- mtkcpu/tests/test_debug_unit.py | 3 +- mtkcpu/tests/test_memory.py | 59 ++++++++++++++++++++++-------- mtkcpu/units/debug/dmi_handlers.py | 2 +- mtkcpu/units/loadstore.py | 56 ++++++++++++++-------------- mtkcpu/units/mmio/ebr.py | 15 +++++--- mtkcpu/utils/tests/dmi_utils.py | 3 +- mtkcpu/utils/tests/utils.py | 51 ++++++++++++++++++++++---- 8 files changed, 130 insertions(+), 61 deletions(-) diff --git a/mtkcpu/cpu/cpu.py b/mtkcpu/cpu/cpu.py index 24e9875..a60e0bf 100755 --- a/mtkcpu/cpu/cpu.py +++ b/mtkcpu/cpu/cpu.py @@ -496,7 +496,7 @@ def fetch_with_new_pc(pc : Signal): comb += [ ibus.en.eq(1), ibus.store.eq(0), - ibus.addr.eq(pc), + ibus.addr.eq(pc >> 2), ibus.mask.eq(0b1111), ibus.is_fetch.eq(1), ] diff --git a/mtkcpu/tests/test_debug_unit.py b/mtkcpu/tests/test_debug_unit.py index c5e20c5..1769690 100755 --- a/mtkcpu/tests/test_debug_unit.py +++ b/mtkcpu/tests/test_debug_unit.py @@ -1089,7 +1089,7 @@ def resume_core_via_dmi(): if __name__ == "__main__": # import pytest # pytest.main(["-x", __file__]) - test_xd() + # test_xd() # test_not_supported_command_type_finishes() # test_dmi_try_read_not_implemented_register() # test_dmi_abstract_command_read_write_gpr() @@ -1103,6 +1103,7 @@ def resume_core_via_dmi(): # test_abstracauto_autoexecdata() # test_ebreakm_halt() # test_single_step() + test_progbuf_writes_to_bus() logging.critical("ALL TESTS PASSED!") diff --git a/mtkcpu/tests/test_memory.py b/mtkcpu/tests/test_memory.py index d2f3d86..cf09ada 100644 --- a/mtkcpu/tests/test_memory.py +++ b/mtkcpu/tests/test_memory.py @@ -152,21 +152,6 @@ def __call__(self, val=None) -> RegistryContents: mem_out=MemoryContents(memory={0x10: Bits(int=-5, length=16).uint}), ), - - # TODO tu jestem - MemTestCase( - name="simple 'sb'", - source_type=MemTestSourceType.TEXT, - source=f""" - .section code - sb x5, 0x11(x{fill_but_one.addr_reg_idx}) - """, - timeout=10, - reg_init=fill_but_one(0xAA), - mem_out=MemoryContents(memory={0x11: 0xAA}), - ), - - MemTestCase( name="overwrite 'sb'", source_type=MemTestSourceType.TEXT, @@ -206,6 +191,50 @@ def __call__(self, val=None) -> RegistryContents: ] +EXDI = [ + # TODO tu jestem + # MemTestCase( + # name="simple 'sb'", + # source_type=MemTestSourceType.TEXT, + # source=f""" + # .section code + # sb x5, 0x11(x1) + # """, + # timeout=10, + # reg_init=fill_but_one(0xAA), + # mem_out=MemoryContents(memory={0x11: 0xAA}), + # ), + + # MemTestCase( + # name="overwrite 'sw'", + # source_type=MemTestSourceType.TEXT, + # source=f""" + # .section code + # sw x5, 0xcc(x{fill_but_one.addr_reg_idx}) + # """, + # timeout=10, + # reg_init=fill_but_one(0xAAAA), + # mem_init=MemoryContents(memory={0xcc: 0xDEADBEEF}), + # mem_out=MemoryContents(memory={0xcc: 0xAAAA}), + # ), + + MemTestCase( + name="overwrite 'sh'", + source_type=MemTestSourceType.TEXT, + source=f""" + .section code + sh x5, 0x10(x{fill_but_one.addr_reg_idx}) + """, + timeout=10, + reg_init=fill_but_one(0xAAAA), + mem_init=MemoryContents(memory={0x10: 0xDEADBEEF}), + mem_out=MemoryContents(memory={0x10: 0xDEADAAAA}), + ), + + +] + @mem_test(MEMORY_TESTS) +# @mem_test(EXDI) def test_memory(_): pass diff --git a/mtkcpu/units/debug/dmi_handlers.py b/mtkcpu/units/debug/dmi_handlers.py index b9cc30a..2167ce0 100644 --- a/mtkcpu/units/debug/dmi_handlers.py +++ b/mtkcpu/units/debug/dmi_handlers.py @@ -295,7 +295,7 @@ def elaborate(self, _): bus.en.eq(1), bus.store.eq(1), bus.write_data.eq(self.write_value), - bus.addr.eq(my_mmio_addr), + bus.addr.eq(my_mmio_addr >> 2), bus.mask.eq(0b1111), ] diff --git a/mtkcpu/units/loadstore.py b/mtkcpu/units/loadstore.py index dab94ed..0f41d5b 100644 --- a/mtkcpu/units/loadstore.py +++ b/mtkcpu/units/loadstore.py @@ -27,7 +27,7 @@ ("en", 1, DIR_FANIN), ("store", 1, DIR_FANIN), ("is_fetch", 1, DIR_FANIN), - ("addr", 32, DIR_FANIN), + ("addr", 30, DIR_FANIN), ("mask", 4, DIR_FANIN), ("write_data", 32, DIR_FANIN), @@ -331,27 +331,14 @@ def elaborate(self, platform): gb = self.generic_bus with m.If(self.decoder.no_match & self.wb_bus.cyc): - m.d.comb += self.exception_unit.badaddr.eq(gb.addr) + m.d.comb += self.exception_unit.badaddr.eq(gb.addr << 2) with m.If(gb.store): m.d.comb += self.exception_unit.m_store_error.eq(1) with m.Elif(gb.is_fetch): m.d.comb += self.exception_unit.m_fetch_error.eq(1) with m.Else(): m.d.comb += self.exception_unit.m_load_error.eq(1) - - with m.FSM(): - with m.State("xd"): - with m.If((gb.addr == 0x800000d6) & gb.store & gb.en): - # verified. proceed to value test - with m.If((gb.mask == 0b001) & (gb.write_data == 0x0)): - m.next = "dx" - with m.State("dx"): - ctr = Signal(20) - if platform: - led_b = platform.request("led_r", 1).o - m.d.sync += ctr.eq(ctr + 1) - with m.If(ctr == 0): - m.d.sync += led_b.eq(~led_b) + with m.If(~pe.none): # transaction request occured @@ -376,7 +363,7 @@ def elaborate(self, platform): sync += first.eq(1) with m.State("REQ"): comb += gb.connect(bus_owner_port, exclude=["addr"]) - comb += gb.addr.eq(phys_addr) # found by page-walk + comb += gb.addr.eq(phys_addr >> 2) # found by page-walk with m.If(first): sync += first.eq(0) with m.Else(): @@ -427,7 +414,7 @@ def error(code: Issue): )) comb += [ gb.en.eq(1), - gb.addr.eq(Cat(Const(0, 2), vpn, root_ppn)), + gb.addr.eq(Cat(vpn, root_ppn)), gb.store.eq(0), gb.mask.eq(0b1111), # TODO use -1 ] @@ -518,7 +505,7 @@ def elaborate(self, platform): # XXX for now we don't use strobe signal (cyc only) comb += [ - wb.adr.eq(gb.addr), + wb.adr.eq(gb.addr << 2), wb.dat_w.eq(gb.write_data), wb.sel.eq(gb.mask), wb.we.eq(gb.store), @@ -578,11 +565,22 @@ def elaborate(self, platform): # allow naturally aligned addresses # TODO trap on wrong address data = Mux(store, self.src2, loadstore.read_data) - comb += [ - word.eq(data), - half_word.eq(data.word_select(addr_lsb[1], 16)), - byte.eq(data.word_select(addr_lsb, 8)), - ] + + with m.If(store): + comb += [ + word.eq(data), + half_word.eq(data[:16]), + byte.eq(data[:8]), + ] + with m.Else(): + # XXX XXX XXX + # TODO i'm not sure about this + comb += [ + word.eq(data), + half_word.eq(data.word_select(addr_lsb[1], 16)), + byte.eq(data.word_select(addr_lsb, 8)), + ] + # TODO choice expression (amaranth-0.6) with m.If(~store): @@ -608,20 +606,20 @@ def elaborate(self, platform): ] with m.Case(Funct3.H, Funct3.HU): comb += [ - write_data.eq(half_word), - mask.eq(0b11 >> addr_lsb[1]), + write_data.eq(half_word << (8 * addr_lsb[1])), + mask.eq(0b11 << addr_lsb[1]), ] with m.Case(Funct3.B, Funct3.BU): comb += [ - write_data.eq(byte), - mask.eq(0b1 >> addr_lsb), + write_data.eq(byte << 8*addr_lsb), + mask.eq(0b1 << addr_lsb), ] with m.If(self.en): comb += [ loadstore.en.eq(1), loadstore.store.eq(store), - loadstore.addr.eq(addr & ~Const(0b11, 32)), + loadstore.addr.eq(addr >> 2), loadstore.mask.eq(mask), loadstore.write_data.eq(write_data), ] diff --git a/mtkcpu/units/mmio/ebr.py b/mtkcpu/units/mmio/ebr.py index fcaa8b2..16c2046 100644 --- a/mtkcpu/units/mmio/ebr.py +++ b/mtkcpu/units/mmio/ebr.py @@ -41,14 +41,17 @@ def handle_transaction(self, wb_slv_module): data = wb_slave.wb_bus.dat_w mask = wb_slave.wb_bus.sel - from math import log2 - ws_bytes = self.mem_config.word_size - assert log2(ws_bytes).is_integer() - ws_bit_shift = Const(int(log2(ws_bytes))) - # raise ValueError(ws_bit_shift) + # from math import log2 + # ws_bytes = self.mem_config.word_size + # assert log2(ws_bytes).is_integer() + # ws_bit_shift = Const(int(log2(ws_bytes))) + # # raise ValueError(ws_bit_shift) + + # real_addr = Signal(32) + # wb_comb += real_addr.eq(addr >> ws_bit_shift) real_addr = Signal(32) - wb_comb += real_addr.eq(addr >> ws_bit_shift) + wb_comb += real_addr.eq(addr >> 2) # WARNING: # that FSM in nested in another one - we have to use Module instance diff --git a/mtkcpu/utils/tests/dmi_utils.py b/mtkcpu/utils/tests/dmi_utils.py index d68979a..175f486 100644 --- a/mtkcpu/utils/tests/dmi_utils.py +++ b/mtkcpu/utils/tests/dmi_utils.py @@ -659,6 +659,7 @@ def f(): data = yield gb.write_data msg = f"MEMORY BUS ACTIVE: addr={hex(addr)}, is_store={store}, data={hex(data)}" logging.critical(msg) - output_dict[addr] = data + addr_adjusted_from_30bit_to_32_bit = addr << 2 + output_dict[addr_adjusted_from_30bit_to_32_bit] = data yield return f diff --git a/mtkcpu/utils/tests/utils.py b/mtkcpu/utils/tests/utils.py index a472160..b8f899f 100644 --- a/mtkcpu/utils/tests/utils.py +++ b/mtkcpu/utils/tests/utils.py @@ -85,15 +85,52 @@ def capture_write_transactions(cpu : MtkCpu, dict_reference : OrderedDict) -> Or def f(): yield Passive() content = dict_reference - wp = cpu.arbiter.ebr.wp - mem = cpu.arbiter.ebr.mem._array + from mtkcpu.units.loadstore import EBR_Wishbone + ebr: EBR_Wishbone = cpu.arbiter.ebr + wp = ebr.wp + mem = ebr.mem._array + + def apply_bitmask(A, B): + assert B == (B & 0b1111) + result = 0 + for i in range(4): + if B & (1 << i): + result |= (A & (0xFF << (i * 8))) + return result + while(True): - en = yield wp.en - addr = yield wp.addr - if en: + mask = yield wp.en + if mask: + wp_addr = yield wp.addr + bus_addr = yield ebr._wb_slave_bus.wb_bus.adr + + # coherence check beween bus and wp + assert bus_addr % 4 == 0 + assert (bus_addr >> 2) == wp_addr + + # find first non-zero bit + if mask & 0b1: + offset = 0 + elif mask & 0b10: + offset = 1 + elif mask & 0b100: + offset = 2 + elif mask & 0b1000: + offset = 3 + else: + assert False + + data_before_write = yield mem[wp_addr] yield - data = yield mem[addr] - content[addr << 2] = data + data_after_write = yield mem[wp_addr] + + # all_ones_u32_mask = apply_bitmask(0xffff_ffff, mask) + # new_data = apply_bitmask(data, mask) + + # real_addr = bus_addr + offset + # assert real_addr not in content + # content[real_addr] = apply_bitmask(data, mask) + content[bus_addr] = data_after_write yield return f From dcd3bb47969a3512d5cc0e4d965ba545c24c8f63 Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Mon, 22 Jul 2024 01:09:21 +0200 Subject: [PATCH 3/8] why gtkw is missing --- mtkcpu/cpu/cpu.py | 33 +++++++++++++---- mtkcpu/tests/test_csr.py | 61 +++++++++++++++++++++++++++++++- mtkcpu/units/csr/csr_handlers.py | 2 +- mtkcpu/units/exception.py | 4 +-- mtkcpu/utils/tests/utils.py | 36 +++++++++---------- 5 files changed, 108 insertions(+), 28 deletions(-) diff --git a/mtkcpu/cpu/cpu.py b/mtkcpu/cpu/cpu.py index a60e0bf..aa483d3 100755 --- a/mtkcpu/cpu/cpu.py +++ b/mtkcpu/cpu/cpu.py @@ -247,13 +247,31 @@ def elaborate(self, platform): mtime = self.mtime = Signal(32) sync += mtime.eq(mtime + 1) comb += csr_unit.mtime.as_view().eq(mtime) + timer_irq_happened = Signal() - # with m.If(csr_unit.mstatus.mie & csr_unit.mie.mtie): - # with m.If(mtime == csr_unit.mtimecmp): - # # 'halt' signal needs to be cleared when CPU jumps to trap handler. - # sync += [ - # self.halt.eq(1), - # ] + + # with m.If(csr_unit.mstatus.as_view().mie & csr_unit.mie.as_view().mtie): + # with m.If(mtime == csr_unit.mtimecmp.as_view().as_value()): + # sync += timer_irq_happened.eq(1) + + ctr = Signal(5, reset=1) + sync += ctr.eq(ctr + 1) + + with m.FSM(): + with m.State("A"): + with m.If(ctr == 0): + m.next = "B" + with m.State("B"): + with m.If(csr_unit.mie.as_view().mtie): + sync += timer_irq_happened.eq(1) + m.next = "C" + with m.State("C"): + pass + + # with m.If(csr_unit.mie.as_view().mtie): + # with m.If(csr_unit.mstatus.as_view().mie): + # with m.If(ctr == 0): + # sync += timer_irq_happened.eq(1) def prev(sig: Signal) -> Signal: res = Signal() @@ -460,6 +478,9 @@ def fetch_with_new_pc(pc : Signal): # NOTE: 'Elif' is not accidental here - HALTREQ has higher priority than STEP. sync += dcsr.as_view().cause.eq(DCSR_DM_Entry_Cause.STEP) m.next = "HALTED" + with m.Elif(timer_irq_happened): + sync += timer_irq_happened.eq(0) # clear the bit + trap(IrqCause.M_TIMER_INTERRUPT, interrupt=True) with m.Else(): # maybe next time.. m.next = "FETCH" diff --git a/mtkcpu/tests/test_csr.py b/mtkcpu/tests/test_csr.py index 296a513..a208f03 100644 --- a/mtkcpu/tests/test_csr.py +++ b/mtkcpu/tests/test_csr.py @@ -283,8 +283,67 @@ ), ] +EXDI = [ + # MemTestCase( + # name="TEST test TEST", + # source_type=MemTestSourceType.RAW, + # source=f""" + # start: + # la x5, trap + # csrw mtvec, x5 + # li x1, 0b1000 # mstatus.mie + # csrw mstatus, x1 + # li x1, 0b10000000 # mie.mtie + # csrw mie, x1 + # loop: + # j loop + # trap: + # csrr x1, mepc + # addi x1, x1, 4 + # csrw mepc, x1 + # mret + # addi x2, x0, 10 + # """, + # out_reg=2, + # out_val=20, + # timeout=100, + # mem_init=MemoryContents.empty(), + # reg_init=RegistryContents.fill(), + # ), + + MemTestCase( + name="timer interrupt", + source_type=MemTestSourceType.RAW, + source=f""" + start: + la x5, trap + csrw mtvec, x5 + csrr x2, {int(CSRNonStandardIndex.MTIME)} + addi x2, x2, 128 # interupt in ~100 cycles + csrw {int(CSRNonStandardIndex.MTIMECMP)}, x2 + + // li x1, 0b10000000 # mie.mtie + li x1, 0xffffffff + csrw mie, x5 + + // li x5, 0b1000 # mstatus.mie + li x1, 0xffffffff + csrw mstatus, x5 + loop: + j loop + trap: + addi x15, x0, 123 + """, + out_reg=15, + out_val=123, + timeout=2000, + mem_init=MemoryContents.empty(), + reg_init=RegistryContents.fill(), + ), +] -@mem_test(CSR_TESTS) +# @mem_test(CSR_TESTS) +@mem_test(EXDI) def test_registers(_): pass diff --git a/mtkcpu/units/csr/csr_handlers.py b/mtkcpu/units/csr/csr_handlers.py index 1a16ea1..4ad495b 100644 --- a/mtkcpu/units/csr/csr_handlers.py +++ b/mtkcpu/units/csr/csr_handlers.py @@ -171,7 +171,7 @@ def elaborate(self, _): return self.latch_whole_value_with_no_side_effect() class MIP(CSR_Write_Handler): - addr = CSRIndex.MIE + addr = CSRIndex.MIP layout = MIP_Layout # TODO diff --git a/mtkcpu/units/exception.py b/mtkcpu/units/exception.py index ef6086c..13a8263 100644 --- a/mtkcpu/units/exception.py +++ b/mtkcpu/units/exception.py @@ -104,8 +104,8 @@ def elaborate(self, platform): mip.meip.eq(self.external_interrupt) ] m.d.sync += [ - # self.mstatus.r.mpie.eq(self.mstatus.r.mie), - # self.mstatus.r.mie.eq(0), + self.mstatus.r.mpie.eq(self.mstatus.r.mie), + self.mstatus.r.mie.eq(0), mepc.eq(self.m_pc) ] with m.If(~trap_pe.n): diff --git a/mtkcpu/utils/tests/utils.py b/mtkcpu/utils/tests/utils.py index b8f899f..91f85fe 100644 --- a/mtkcpu/utils/tests/utils.py +++ b/mtkcpu/utils/tests/utils.py @@ -195,7 +195,7 @@ def reg_test( # *csr_unit.mepc.fields.values(), # *csr_unit.mcause.members.values(), # *csr_unit.satp.fields.values(), - # *csr_unit.mie.fields.values(), + *csr_unit.mie.fields.values(), # *csr_unit.mstatus.fields.values(), # *csr_unit.mtime.fields.values(), # *csr_unit.mtimecmp.fields.values(), @@ -208,23 +208,23 @@ def reg_test( # 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, + # 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 From d949a61b8e7daa4dcee62e09306638087483e4a1 Mon Sep 17 00:00:00 2001 From: Matuesz Date: Mon, 22 Jul 2024 17:29:55 +0200 Subject: [PATCH 4/8] test passes --- mtkcpu/cpu/cpu.py | 31 ++++++++----------------------- mtkcpu/tests/test_csr.py | 10 ++++------ mtkcpu/units/exception.py | 4 ++-- mtkcpu/units/mmio/bspgen.py | 9 +++++++++ mtkcpu/utils/tests/utils.py | 4 ++-- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/mtkcpu/cpu/cpu.py b/mtkcpu/cpu/cpu.py index aa483d3..3f9b1f2 100755 --- a/mtkcpu/cpu/cpu.py +++ b/mtkcpu/cpu/cpu.py @@ -249,29 +249,10 @@ def elaborate(self, platform): comb += csr_unit.mtime.as_view().eq(mtime) timer_irq_happened = Signal() - - # with m.If(csr_unit.mstatus.as_view().mie & csr_unit.mie.as_view().mtie): - # with m.If(mtime == csr_unit.mtimecmp.as_view().as_value()): - # sync += timer_irq_happened.eq(1) - - ctr = Signal(5, reset=1) - sync += ctr.eq(ctr + 1) - - with m.FSM(): - with m.State("A"): - with m.If(ctr == 0): - m.next = "B" - with m.State("B"): - with m.If(csr_unit.mie.as_view().mtie): - sync += timer_irq_happened.eq(1) - m.next = "C" - with m.State("C"): - pass - - # with m.If(csr_unit.mie.as_view().mtie): - # with m.If(csr_unit.mstatus.as_view().mie): - # with m.If(ctr == 0): - # sync += timer_irq_happened.eq(1) + # Timer IRQ delivery. + with m.If(csr_unit.mstatus.as_view().mie & csr_unit.mie.as_view().mtie): + with m.If(mtime == csr_unit.mtimecmp.as_view().as_value()): + sync += timer_irq_happened.eq(1) def prev(sig: Signal) -> Signal: res = Signal() @@ -479,6 +460,10 @@ def fetch_with_new_pc(pc : Signal): sync += dcsr.as_view().cause.eq(DCSR_DM_Entry_Cause.STEP) m.next = "HALTED" with m.Elif(timer_irq_happened): + # TODO from specs: + # "The interrupt remains posted until it clears by writing the MTIMECMP register" + # + # For now we don't implement that part of specs, just clear the bit by default. sync += timer_irq_happened.eq(0) # clear the bit trap(IrqCause.M_TIMER_INTERRUPT, interrupt=True) with m.Else(): diff --git a/mtkcpu/tests/test_csr.py b/mtkcpu/tests/test_csr.py index a208f03..b30d2dc 100644 --- a/mtkcpu/tests/test_csr.py +++ b/mtkcpu/tests/test_csr.py @@ -322,13 +322,11 @@ addi x2, x2, 128 # interupt in ~100 cycles csrw {int(CSRNonStandardIndex.MTIMECMP)}, x2 - // li x1, 0b10000000 # mie.mtie - li x1, 0xffffffff - csrw mie, x5 + li x1, 0b10000000 # mie.mtie + csrw mie, x1 - // li x5, 0b1000 # mstatus.mie - li x1, 0xffffffff - csrw mstatus, x5 + li x1, 0b1000 # mstatus.mie + csrw mstatus, x1 loop: j loop trap: diff --git a/mtkcpu/units/exception.py b/mtkcpu/units/exception.py index 13a8263..890dfe7 100644 --- a/mtkcpu/units/exception.py +++ b/mtkcpu/units/exception.py @@ -104,8 +104,8 @@ def elaborate(self, platform): mip.meip.eq(self.external_interrupt) ] m.d.sync += [ - self.mstatus.r.mpie.eq(self.mstatus.r.mie), - self.mstatus.r.mie.eq(0), + mstatus.mpie.eq(mstatus.mie), + mstatus.mie.eq(0), mepc.eq(self.m_pc) ] with m.If(~trap_pe.n): diff --git a/mtkcpu/units/mmio/bspgen.py b/mtkcpu/units/mmio/bspgen.py index 55e95f4..6e72598 100644 --- a/mtkcpu/units/mmio/bspgen.py +++ b/mtkcpu/units/mmio/bspgen.py @@ -95,4 +95,13 @@ def gen_bsp_sources(owners : List['BusSlaveOwnerInterface'], addr_schemes : List __class__.__generate_cc(c, s) log(f"generating {__class__.get_h_path(s)}") __class__.__generate_h(c, s) + csr_path = Path(f"{__class__.gen_dir}/csr.h") + + log(f"generating {csr_path}") + csr_text_lines = ["// autogenerated by 'gen_bsp' pass"] + from mtkcpu.cpu.priv_isa import CSRNonStandardIndex + for x in CSRNonStandardIndex: + csr_text_lines.append(f"#define {x.name}_CSR_REG_ADDR {hex(x.value)}") + + csr_path.write_text("\n".join(csr_text_lines)) log("ok, code generation done!") diff --git a/mtkcpu/utils/tests/utils.py b/mtkcpu/utils/tests/utils.py index 91f85fe..014446c 100644 --- a/mtkcpu/utils/tests/utils.py +++ b/mtkcpu/utils/tests/utils.py @@ -171,7 +171,7 @@ def reg_test( # 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, @@ -195,7 +195,7 @@ def reg_test( # *csr_unit.mepc.fields.values(), # *csr_unit.mcause.members.values(), # *csr_unit.satp.fields.values(), - *csr_unit.mie.fields.values(), + # csr_unit.mie.as_view().mtie, # *csr_unit.mstatus.fields.values(), # *csr_unit.mtime.fields.values(), # *csr_unit.mtimecmp.fields.values(), From 64ec7ebb506f9f93d81d10f75277c0bf8f02dd1a Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sun, 18 Aug 2024 01:09:49 +0200 Subject: [PATCH 5/8] reverse unecessary changes --- board/breakpoint_example.gdb | 5 +- mtkcpu/tests/test_debug_unit.py | 103 ++++---------------------------- mtkcpu/tests/test_memory.py | 39 +++++++----- 3 files changed, 38 insertions(+), 109 deletions(-) diff --git a/board/breakpoint_example.gdb b/board/breakpoint_example.gdb index 0d1618a..f39c8cb 100644 --- a/board/breakpoint_example.gdb +++ b/board/breakpoint_example.gdb @@ -4,5 +4,6 @@ set riscv use-compressed-breakpoints no target extended-remote localhost:3333 set mem inaccessible-by-default off set remotetimeout 10 -# load -# run +load +b main +run diff --git a/mtkcpu/tests/test_debug_unit.py b/mtkcpu/tests/test_debug_unit.py index 67c3e30..f9c7566 100755 --- a/mtkcpu/tests/test_debug_unit.py +++ b/mtkcpu/tests/test_debug_unit.py @@ -548,83 +548,6 @@ def main_process(): simulator.run() -@dmi_simulator -def test_xd( - simulator: Simulator, - cpu: MtkCpu, - dmi_monitor: DMI_Monitor, -): - assert PROGBUFSIZE >= 2 - - def main_process(): - yield from few_ticks() - - val = 123 - assert val < 2**11 - - gpr_reg_num = 8 - - for i, ins in enumerate([ - # addi x1, x0, - encode_ins(instructions.Addi(registers.get_register(gpr_reg_num), registers.get_register(0), val)), - encode_ins(instructions.Ebreak()), - ]): - yield from progbuf_write_wait_for_success(dmi_monitor, i, ins) - - yield from trigger_progbuf_exec(dmi_monitor=dmi_monitor) - yield from dmi_op_wait_for_cmderr( - dmi_monitor=dmi_monitor, - expected_cmderr=ABSTRACTCS_Layout.CMDERR.HALT_OR_RESUME, - timeout=1000, - ) - - yield from clear_cmderr_wait_for_success(dmi_monitor=dmi_monitor) - - yield from activate_DM_and_halt_via_dmi(dmi_monitor=dmi_monitor) - halted = yield from cpu_core_is_halted(dmi_monitor=dmi_monitor) - if not halted: - raise ValueError("CPU was not halted after haltreq set!") - - yield from trigger_progbuf_exec(dmi_monitor=dmi_monitor) - yield from dmi_op_wait_for_success(dmi_monitor=dmi_monitor, timeout=100) - - x = yield cpu.regs._array._inner[gpr_reg_num] - - if x != val: - raise ValueError(f"expected x{gpr_reg_num} to contain {hex(val)}, got {hex(x)} instead") - - # make sure that after successfull PROGBUF execution, PC is properly restored. - - def resume_core_via_dmi(): - yield from DMCONTROL_setup_basic_fields(dmi_monitor=dmi_monitor, dmi_op=DMIOp.WRITE) - yield dmi_monitor.cur_DMCONTROL.haltreq.eq(0) - yield dmi_monitor.cur_DMCONTROL.resumereq.eq(1) - yield from dmi_bus_trigger_transaction(dmi_monitor=dmi_monitor) - yield from dmi_op_wait_for_success(dmi_monitor=dmi_monitor) - - yield from resume_core_via_dmi() - - pc = yield cpu.pc - min_incl, max_excl = PROGBUF_MMIO_ADDR, PROGBUF_MMIO_ADDR + 4 * PROGBUFSIZE - progbuf_pc_range = range(min_incl, max_excl, 4) - assert MEM_START_ADDR not in progbuf_pc_range - if pc in progbuf_pc_range: - raise ValueError(f"After successfull PROGBUF execution hart was sent resumereq, but the PC was not updated back! pc={hex(pc)}") - - processes = [ - main_process, - monitor_cpu_and_dm_state(dmi_monitor=dmi_monitor), - bus_capture_write_transactions(cpu=cpu, output_dict=dict()), - monitor_pc_and_main_fsm(cpu=cpu, wait_for_first_haltreq=False), - ] - - for p in processes: - simulator.add_sync_process(p) - - simulator.run() - - - @dmi_simulator def test_progbuf_gets_executed( simulator: Simulator, @@ -1089,21 +1012,19 @@ def resume_core_via_dmi(): if __name__ == "__main__": # import pytest # pytest.main(["-x", __file__]) - # test_xd() - # test_not_supported_command_type_finishes() - # test_dmi_try_read_not_implemented_register() - # test_dmi_abstract_command_read_write_gpr() - # test_core_halt_resume() - # test_halt_resume_with_new_dpc() - # test_cmderr_clear() - # test_progbuf_writes_to_bus() - # test_progbuf_gets_executed() - # test_progbuf_cmderr_on_runtime_error() - # test_access_debug_csr_regs_in_debug_mode() - # test_abstracauto_autoexecdata() - # test_ebreakm_halt() - # test_single_step() + test_not_supported_command_type_finishes() + test_dmi_try_read_not_implemented_register() + test_dmi_abstract_command_read_write_gpr() + test_core_halt_resume() + test_halt_resume_with_new_dpc() + test_cmderr_clear() test_progbuf_writes_to_bus() + test_progbuf_gets_executed() + test_progbuf_cmderr_on_runtime_error() + test_access_debug_csr_regs_in_debug_mode() + test_abstracauto_autoexecdata() + test_ebreakm_halt() + test_single_step() logging.critical("ALL TESTS PASSED!") diff --git a/mtkcpu/tests/test_memory.py b/mtkcpu/tests/test_memory.py index d6c01ab..97ee7e7 100644 --- a/mtkcpu/tests/test_memory.py +++ b/mtkcpu/tests/test_memory.py @@ -138,21 +138,18 @@ def __call__(self, val=None) -> RegistryContents: reg_init=fill_but_one(), mem_init=MemoryContents(memory={0x20: 0xdeadbeef}), ), - # TODO tu jestem - # MemTestCase( - # name="simple 'sh'", - # source_type=MemTestSourceType.TEXT, - # source=f""" - # .section code - # lui x5, 0xaabbf - # addi x5, x5, 0x01cd - # sh x5, 0x10(x{fill_but_one.addr_reg_idx}) - # """, - # timeout=10, - # reg_init=fill_but_one(), - # mem_init=MemoryContents(memory={0x10: 0}), - # mem_out=MemoryContents(memory={0x10: 0}), - # ), + MemTestCase( + name="simple 'sh'", + source_type=MemTestSourceType.TEXT, + source=f""" + .section code + sh x5, 0x10(x{fill_but_one.addr_reg_idx}) + """, + timeout=10, + reg_init=fill_but_one(), + mem_init=MemoryContents(memory={0x10: 5}), + mem_out=MemoryContents(memory={0x10: 5}), + ), MemTestCase( name="negative 'sh'", source_type=MemTestSourceType.TEXT, @@ -164,7 +161,17 @@ def __call__(self, val=None) -> RegistryContents: reg_init=fill_but_one(-5), mem_out=MemoryContents(memory={0x10: Bits(int=-5, length=16).uint}), ), - + MemTestCase( + name="simple 'sb'", + source_type=MemTestSourceType.TEXT, + source=f""" + .section code + sb x5, 0x10(x{fill_but_one.addr_reg_idx}) + """, + timeout=10, + reg_init=fill_but_one(0xAA), + mem_out=MemoryContents(memory={0x10: 0xAA}), + ), MemTestCase( name="overwrite 'sb'", source_type=MemTestSourceType.TEXT, From bf5dc21000803b146f0739b4181f4d7ecbfa577e Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sun, 18 Aug 2024 01:13:49 +0200 Subject: [PATCH 6/8] test also mcause --- mtkcpu/tests/test_csr.py | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/mtkcpu/tests/test_csr.py b/mtkcpu/tests/test_csr.py index b30d2dc..efb0a83 100644 --- a/mtkcpu/tests/test_csr.py +++ b/mtkcpu/tests/test_csr.py @@ -284,33 +284,6 @@ ] EXDI = [ - # MemTestCase( - # name="TEST test TEST", - # source_type=MemTestSourceType.RAW, - # source=f""" - # start: - # la x5, trap - # csrw mtvec, x5 - # li x1, 0b1000 # mstatus.mie - # csrw mstatus, x1 - # li x1, 0b10000000 # mie.mtie - # csrw mie, x1 - # loop: - # j loop - # trap: - # csrr x1, mepc - # addi x1, x1, 4 - # csrw mepc, x1 - # mret - # addi x2, x0, 10 - # """, - # out_reg=2, - # out_val=20, - # timeout=100, - # mem_init=MemoryContents.empty(), - # reg_init=RegistryContents.fill(), - # ), - MemTestCase( name="timer interrupt", source_type=MemTestSourceType.RAW, @@ -330,10 +303,11 @@ loop: j loop trap: - addi x15, x0, 123 + csrr x14, mcause + andi x15, x14, 0xff """, out_reg=15, - out_val=123, + out_val=IrqCause.M_TIMER_INTERRUPT, timeout=2000, mem_init=MemoryContents.empty(), reg_init=RegistryContents.fill(), From 8a5bec3c82ddf276429006b75464ef2860388159 Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sun, 18 Aug 2024 01:36:23 +0200 Subject: [PATCH 7/8] work on another timer test. --- mtkcpu/tests/test_csr.py | 80 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/mtkcpu/tests/test_csr.py b/mtkcpu/tests/test_csr.py index efb0a83..68d0bb7 100644 --- a/mtkcpu/tests/test_csr.py +++ b/mtkcpu/tests/test_csr.py @@ -285,7 +285,7 @@ EXDI = [ MemTestCase( - name="timer interrupt", + name="timer interrupt happens with proper mcause", source_type=MemTestSourceType.RAW, source=f""" start: @@ -312,6 +312,84 @@ mem_init=MemoryContents.empty(), reg_init=RegistryContents.fill(), ), + + # "The interrupt remains posted until it clears by writing the MTIMECMP register" - privileged specs. + MemTestCase( + name="timer interrupt is cleared only with 'mtimecmp' write", + source_type=MemTestSourceType.RAW, + source=f""" +start: + la x5, trap + csrw mtvec, x5 + csrr x2, {int(CSRNonStandardIndex.MTIME)} + addi x2, x2, 128 # interupt in ~100 cycles + csrw {int(CSRNonStandardIndex.MTIMECMP)}, x2 + + li x1, 0b10000000 # mie.mtie + csrw mie, x1 + + li x1, 0b1000 # mstatus.mie + csrw mstatus, x1 +loop: + j loop + +trap: + + // error codes on too_bad entry: + // 1 - NON-TIMER TRAP + // 2 - UNREACHABLE CODE + // 3 - MRET CLEARS TIMER IRQ (IT SHOULDN'T, DUE TO SPECS.) + + + // filter out non-timer related traps. + csrr x5, mcause + andi x5, x5, 0xff + li x6, {IrqCause.M_TIMER_INTERRUPT} + + li x20, 1 + bne x5, x6, too_bad + + addi x10, x10, 1 + + li x11, 1 + beq x10, x11, 1f + li x11, 2 + beq x10, x11, 2f + li x11, 3 + beq x10, x11, 3f + + add x20, x0, x10 + // li x20, 2 + jump too_bad, x3 // should not be reached + +1: + li x20, 3 + la x5, too_bad // should not be executed, as we haven't yet written to 'mtimecmp'. + csrw mepc, x5 + mret + +2: + csrr x1, {int(CSRNonStandardIndex.MTIME)} + addi x1, x0, 1000 + csrw {int(CSRNonStandardIndex.MTIMECMP)}, x1 + la x5, loop + csrw mepc, x5 + mret + +3: + jump ok, x3 + +too_bad: // exit code in x20 + addi x15, x20, 0 +ok: + addi x15, x0, 100 + """, + out_reg=15, + out_val=100, + timeout=2000, + mem_init=MemoryContents.empty(), + reg_init=RegistryContents.fill(), + ), ] From cf079b9184f93b598afd19d4c637b1c18f4a1c8f Mon Sep 17 00:00:00 2001 From: Mateusz Bieganski Date: Sun, 18 Aug 2024 21:45:56 +0200 Subject: [PATCH 8/8] timer irq second test - reached 'mret clears' failure (exit code 3) --- mtkcpu/tests/test_csr.py | 2 +- mtkcpu/utils/tests/sim_tests.py | 5 +++-- mtkcpu/utils/tests/utils.py | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mtkcpu/tests/test_csr.py b/mtkcpu/tests/test_csr.py index 68d0bb7..3e935d5 100644 --- a/mtkcpu/tests/test_csr.py +++ b/mtkcpu/tests/test_csr.py @@ -388,7 +388,7 @@ out_val=100, timeout=2000, mem_init=MemoryContents.empty(), - reg_init=RegistryContents.fill(), + reg_init=RegistryContents.empty(), ), ] diff --git a/mtkcpu/utils/tests/sim_tests.py b/mtkcpu/utils/tests/sim_tests.py index 9788c9c..962ae15 100644 --- a/mtkcpu/utils/tests/sim_tests.py +++ b/mtkcpu/utils/tests/sim_tests.py @@ -290,7 +290,7 @@ def reg_test(timeout=default_timeout_extra + timeout_cycles, expected_val=expect yield Tick() yield Settle() - for _ in range(timeout): + for i in range(timeout): en = yield cpu.reg_write_port.en if en == 1: addr = yield cpu.reg_write_port.addr @@ -311,9 +311,10 @@ def reg_test(timeout=default_timeout_extra + timeout_cycles, expected_val=expect if check_reg_content and cond: # TODO that mechanism for now allows for only one write to observed register per test, # extend it if neccessary. + pc = yield cpu.pc print( f"== ERROR: Expected data write to reg x{addr} of value {hex(expected_val)}," - f" got value {hex(val)}.. \n== fail test: {name}\n" + f" got value {hex(val)} in cycle={i}, PC={hex(pc)}.. \n== fail test: {name}\n" ) print( f"{format(expected_val, '32b')} vs {format(val, '32b')}" diff --git a/mtkcpu/utils/tests/utils.py b/mtkcpu/utils/tests/utils.py index df6ffb2..a7d470c 100644 --- a/mtkcpu/utils/tests/utils.py +++ b/mtkcpu/utils/tests/utils.py @@ -210,6 +210,8 @@ def reg_test( sim.add_sync_process(capture_write_transactions(cpu=cpu, dict_reference=result_mem)) # sim.add_sync_process(print_mem_transactions(cpu=cpu)) sim.add_sync_process(check_addr_translation_errors(cpu=cpu)) + + sim.add_sync_process(monitor_pc_and_main_fsm(cpu=cpu, wait_for_first_haltreq=False, log_fn=print)) sim.add_sync_process( get_sim_register_test( @@ -292,7 +294,8 @@ def get_code_mem(case: MemTestCase, mem_size_kb: int) -> MemoryContents: import tempfile with tempfile.NamedTemporaryFile( suffix=".elf", - dir=Path(__file__).parent + dir=Path(__file__).parent, + delete=False ) as tmp_elf: source = f""" .global start