for my code import pyrtl

main_memory = pyrtl.MemBlock(bitwidth=32, addrwidth=32, name="main_mem", asynchronous= True)

virtual_addr_i = pyrtl.Input(bitwidth=32, name="virtual_addr_i") new_req_i = pyrtl.Input(bitwidth=1, name="new_req_i") reset_i = pyrtl.Input(bitwidth=1, name="reset_i") req_type_i = pyrtl.Input(bitwidth=1, name="req_type_i")

physical_addr_o = pyrtl.Output(bitwidth=32,name="physical_addr_o") dirty_o = pyrtl.Output(bitwidth=1, name="dirty_o") valid_o = pyrtl.Output(bitwidth=1, name="valid_o") ref_o = pyrtl.Output(bitwidth=1, name="ref_o") error_code_o = pyrtl.Output(bitwidth=3, name="error_code_o") finished_walk_o = pyrtl.Output(bitwidth=1, name="finished_walk_o")

page_fault = pyrtl.WireVector(bitwidth=1, name="page_fault") state = pyrtl.Register(bitwidth=2, name="state") base_register = pyrtl.Const(0x3FFBFF, bitwidth=22)
Step 1 : Split input into the three offsets

writable_o = pyrtl.WireVector(bitwidth=1, name="writable_o") readable_o = pyrtl.WireVector(bitwidth=1, name="readable_o") saved_entry1 = pyrtl.Register(bitwidth=32, name="saved_entry1") saved_entry2 = pyrtl.Register(bitwidth=32, name="saved_entry2") mem_read_addr = pyrtl.WireVector(bitwidth=32, name = "read_addr")

offset3 = virtual_addr_i[0:12] # physical address offset2 = virtual_addr_i[12:22] # second walk offset1 = virtual_addr_i[22:32] # first walk

levelOneAddress = pyrtl.concat(base_register, offset1) levelTwoBase = saved_entry1[0:22] levelTwoAddress = pyrtl.concat(levelTwoBase, offset2)

with pyrtl.conditional_assignment: with state == 1: mem_read_addr |= levelOneAddress with state == 2: mem_read_addr |= levelTwoAddress with pyrtl.otherwise: mem_read_addr |= 0

current_entry = main_memory[mem_read_addr]
Step 2 : UPDATE STATE according to state diagram in instructions

with pyrtl.conditional_assignment: with state == 1: page_fault |= ~current_entry[31] with state == 2: page_fault |= ~current_entry[31]

with pyrtl.conditional_assignment: with state == 0: with new_req_i: state.next |= 1 with reset_i: state.next |= 0 with state == 1: with page_fault: state.next |= 0 with ~page_fault: saved_entry1.next |= current_entry state.next |= 2 with reset_i: state.next |= 0 with state == 2: with page_fault: state.next |= 0 with ~page_fault: saved_entry2.next |= current_entry state.next |= 0 with reset_i: state.next |= 0
Step 3 : Determine physical address by walking the page table structure

physicalBase = current_entry[0:20] physicalAddress = pyrtl.concat(physicalBase, offset3)
Step 4 : Determine the outputs based on the last level of the page table walk

with pyrtl.conditional_assignment: with state == 0: physical_addr_o |= 0 dirty_o |= current_entry[30] valid_o |= current_entry[31] ref_o |= current_entry[29] error_code_o |= 0 finished_walk_o |= 0 with state == 1: with page_fault: finished_walk_o |= 1 error_code_o |= pyrtl.Const(0b001) with ~page_fault: physical_addr_o |= 0 dirty_o |= current_entry[30] valid_o |= current_entry[31] ref_o |= current_entry[29] error_code_o |= 0 finished_walk_o |= 0 with state == 2: dirty_o |= current_entry[30] valid_o |= current_entry[31] ref_o |= current_entry[29] physical_addr_o |= pyrtl.concat(current_entry[0:20], offset3) writable_o |= current_entry[28] readable_o |= current_entry[27] with page_fault: finished_walk_o |= 1 error_code_o |= pyrtl.Const(0b001) with ~page_fault: with req_type_i == 0: with ~readable_o: finished_walk_o |= 1 error_code_o |= pyrtl.Const(0b100) with pyrtl.otherwise: finished_walk_o |= 1 error_code_o |= 0 with req_type_i == 1: with ~writable_o: finished_walk_o |= 1 error_code_o |= pyrtl.Const(0b010) with pyrtl.otherwise: finished_walk_o |= 1 error_code_o |= 0 write me two test cases to test page faults in state1 and state 2 in the below format if name == "main":

"""
These memory addresses correspond to the test that we walk through in the instructions
This just does a basic walk from the first level to the last level where no errors should occur
"""
memory = {
    4293918528: 0xC43FFC6B,
    4294029192: 0xAC061D26,
    1641180595: 0xDEADBEEF
}

sim_trace = pyrtl.SimulationTrace()
sim = pyrtl.Simulation(tracer=sim_trace, memory_value_map={main_memory: memory})

for i in range(3):
    sim.step({
        new_req_i: 1,
        reset_i: 0,
        virtual_addr_i: 0xD0388DB3,
        req_type_i: 0
})

sim_trace.render_trace(symbol_len=20)

assert (sim_trace.trace["physical_addr_o"][-1] == 0x61d26db3)
assert (sim_trace.trace["error_code_o"][-1] == 0x0)
assert (sim_trace.trace["dirty_o"][-1] == 0x0)
assert (sim_trace.trace["readable_o"][-1] == 0x1)