This repository provides an open-source SystemVerilog implementation of the Memory Protection Table (MPT), compliant with the official RISC-V Privileged Specification. The MPT is designed to work with both 32-bit (RV32) and 64-bit (RV64) systems. The MPT is a hardware unit that extends the concept of memory protection by adding fine-grained permission control over physical memory regions. It maps physical page addresses to access permissions based on the supervisor domain ID of the current execution context.
This project was verified on Ubuntu 22.04 and the following tools version:
| Tool | Verified version |
|---|---|
| Vivado | 2024.2 |
| g++ | 11.4.0 |
| GTKWave | 3.3.104 |
| Verilator | 4.038 |
Before starting any simulation, you need to configure the environment for your target architecture (RV32 or RV64).
Run the following command from the root directory:
$ source settings.sh rv32 # For 32-bit systems
$ source settings.sh rv64 # For 64-bit systemsIf no argument is provided (i.e., if the script is executed as source settings.sh), the environment will be configured by default for the RV64 architecture.
Follow these steps to generate test cases, build, run, and verify the MPT simulation:
⚠️ Important: The current version of the simulation test flow in this branch is not compatible with thegenerate_test_cases.pyandcheck_test_cases.pyscripts.
To use the test generation and checking functionalities, you must revert to commitd42df3d.
For more details, refer to the following issue: #7 (comment).
Navigate to the simulation directory and run the generate_test_cases.py script to generate the test cases. Specify the number of test cases as an argument:
$ cd simulation
$ python3 generate_test_cases.py <num_test_cases>This will create a file named test_cases_rv64.csv (for 64-bit systems) or test_cases_rv32.csv (for 32-bit systems) containing randomly generated test cases. Each test case includes the following fields:
mmpt_regspa_iaccess_type_iflush_iallow_oformat_error_oaccess_page_fault_omptl2_entrymptl1_entry
After generating the test cases, compile the simulation with the following command:
$ make buildThis will produce the mpt_top.elf file.
Execute the compiled simulation with:
$ ./mpt_top.elfThe simulation will take the generated test cases as input and produce an output file named simulation_output_rv64.csv (or simulation_output_rv32.csv depending on your architecture). This output file will contain the actual values for the same fields generated in the test cases.
⚠️ Important: The current version of the simulation test flow in this branch is not compatible with thegenerate_test_cases.pyandcheck_test_cases.pyscripts.
To use the test generation and checking functionalities, you must revert to commitd42df3d.
For more details, refer to the following issue: #7 (comment).
To compare the actual simulation output with the expected results (the "oracle" from the generated test cases), use the check_test_cases.py script:
$ python3 check_test_cases.pyThis will verify if the values in the simulation output match the expected values from the test case generation process and generate an output file named test_cases_results_rv64.csv for 64-bit systems.
If you want to visually inspect the internal signal behavior during the simulation, you can generate a waveform using GTKWave:
$ make waveThe Memory Protection Table operates after the address translation stage performed by the Memory Management Unit. Once the virtual address is translated into a physical address, the MPT is queried to retrieve the access permissions associated with the corresponding physical page. These permissions are domain-specific and ensure that memory accesses respect the security policies defined for each supervisor domain. The MPT interfaces with the main memory using a dedicated protocol named mem, which is widely used across the system architecture.
clk_i,rst_i: Standard clock/reset.flush_i: Flushes internal state.ptw_enable_i: Starts the MPT walking process.addr_valid_i: Indicates that spa_i is valid.
mmpt_reg_i: Contains control info such as the root PPN of the MPT and the Supervisor Domain ID (SDID).
The MPT interacts with memory through a memory interface based on the mem protocol. The interface is instantiated using the DEFINE_MEM_MASTER_PORTS macro which expands to:
m_mem_reqm_mem_gntm_mem_validm_mem_addrm_mem_rdatam_mem_wdatam_mem_wem_mem_bem_mem_error
ptw_busy_o: High while the walk is ongoingptw_valid_o: High when PTW completes successfully.
spa_i: The Supervisor Physical Address.access_type_i: Specifies the type of access (read, write, execute).
plb_entry_o: Permission lookaside buffer entry.allow_o: High if access is allowed.access_page_fault_o: High if access is denied.format_error_o:Indicates format issues.