mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 05:37:16 -04:00
Update PTW to latest specification (1.11 WIP)
This commit is contained in:
parent
c04ca3eed7
commit
74aad5fbae
6 changed files with 99 additions and 67 deletions
|
@ -370,7 +370,7 @@ module ariane
|
|||
.csr_valid_o ( csr_valid_ex_id ),
|
||||
.csr_addr_o ( csr_addr_ex_csr ),
|
||||
.csr_commit_i ( csr_commit_commit_ex ), // from commit
|
||||
// memory management
|
||||
// Memory Management
|
||||
.enable_translation_i ( enable_translation_csr_ex ), // from CSR
|
||||
.fetch_req_i ( fetch_req_if_ex ),
|
||||
.fetch_gnt_o ( fetch_gnt_ex_if ),
|
||||
|
@ -379,6 +379,7 @@ module ariane
|
|||
.fetch_rdata_o ( fetch_rdata_ex_if ),
|
||||
.fetch_ex_o ( fetch_ex_ex_if ), // fetch exception to IF
|
||||
.priv_lvl_i ( priv_lvl ), // from CSR
|
||||
.ld_st_priv_lvl_i ( ld_st_priv_lvl_csr_ex ), // from CSR
|
||||
.sum_i ( sum_csr_ex ), // from CSR
|
||||
.mxr_i ( mxr_csr_ex ), // from CSR
|
||||
.satp_ppn_i ( satp_ppn_csr_ex ), // from CSR
|
||||
|
@ -430,7 +431,7 @@ module ariane
|
|||
.eret_o ( eret ),
|
||||
.trap_vector_base_o ( trap_vector_base_commit_pcgen ),
|
||||
.priv_lvl_o ( priv_lvl ),
|
||||
|
||||
.ld_st_priv_lvl_o ( ld_st_priv_lvl_csr_ex ),
|
||||
.enable_translation_o ( enable_translation_csr_ex ),
|
||||
.sum_o ( sum_csr_ex ),
|
||||
.mxr_o ( mxr_csr_ex ),
|
||||
|
|
|
@ -51,6 +51,7 @@ module csr_regfile #(
|
|||
output priv_lvl_t priv_lvl_o, // Current privilege level the CPU is in
|
||||
// MMU
|
||||
output logic enable_translation_o, // Enable VA translation
|
||||
output priv_lvl_t ld_st_priv_lvl_o, // Privilege level at which load and stores should happen
|
||||
output logic sum_o,
|
||||
output logic mxr_o,
|
||||
// input logic flag_mprv_i,
|
||||
|
@ -76,6 +77,12 @@ module csr_regfile #(
|
|||
logic csr_we, csr_read;
|
||||
logic [63:0] csr_wdata, csr_rdata;
|
||||
priv_lvl_t trap_to_priv_lvl;
|
||||
|
||||
// ----------------------
|
||||
// LD/ST Privilege Level
|
||||
// ----------------------
|
||||
assign ld_st_priv_lvl_o = (mstatus_q.mprv) ? mstatus_q.mpp : priv_lvl_o;
|
||||
|
||||
// ----------------
|
||||
// CSR Registers
|
||||
// ----------------
|
||||
|
|
|
@ -78,6 +78,7 @@ module ex_stage #(
|
|||
output logic [31:0] fetch_rdata_o,
|
||||
output exception fetch_ex_o,
|
||||
input priv_lvl_t priv_lvl_i,
|
||||
input priv_lvl_t ld_st_priv_lvl_i,
|
||||
input logic sum_i,
|
||||
input logic mxr_i,
|
||||
input logic [43:0] satp_ppn_i,
|
||||
|
|
|
@ -48,6 +48,7 @@ module lsu #(
|
|||
output exception fetch_ex_o, // Instruction fetch interface
|
||||
|
||||
input priv_lvl_t priv_lvl_i, // From CSR register file
|
||||
input priv_lvl_t ld_st_priv_lvl_i, // From CSR register file
|
||||
input logic sum_i, // From CSR register file
|
||||
input logic mxr_i, // From CSR register file
|
||||
input logic [43:0] satp_ppn_i, // From CSR register file
|
||||
|
@ -193,6 +194,7 @@ module lsu #(
|
|||
) mmu_i (
|
||||
// misaligned bypass
|
||||
.misaligned_ex_i ( misaligned_exception ),
|
||||
.lsu_is_store_i ( st_translation_req ),
|
||||
.lsu_req_i ( translation_req ),
|
||||
.lsu_vaddr_i ( mmu_vaddr ),
|
||||
.lsu_valid_o ( translation_valid ),
|
||||
|
|
102
src/mmu.sv
102
src/mmu.sv
|
@ -41,14 +41,16 @@ module mmu #(
|
|||
// this is a more minimalistic interface because the actual addressing logic is handled
|
||||
// in the LSU as we distinguish load and stores, what we do here is simple address translation
|
||||
input exception misaligned_ex_i,
|
||||
input logic lsu_req_i,
|
||||
input logic [63:0] lsu_vaddr_i,
|
||||
input logic lsu_req_i, // request address translation
|
||||
input logic [63:0] lsu_vaddr_i, // virtual address in
|
||||
input logic lsu_is_store_i, // the translation is requested by a store
|
||||
// if we need to walk the page table we can't grant in the same cycle
|
||||
output logic lsu_valid_o, // translation is valid
|
||||
output logic [63:0] lsu_paddr_o, // translated address
|
||||
output exception lsu_exception_o,
|
||||
output logic lsu_valid_o, // translation is valid
|
||||
output logic [63:0] lsu_paddr_o, // translated address
|
||||
output exception lsu_exception_o, // address translation threw an exception
|
||||
// General control signals
|
||||
input priv_lvl_t priv_lvl_i,
|
||||
input priv_lvl_t ld_st_priv_lvl_i,
|
||||
input logic sum_i,
|
||||
input logic mxr_i,
|
||||
// input logic flag_mprv_i,
|
||||
|
@ -83,7 +85,10 @@ module mmu #(
|
|||
assign instr_if_address_o = fetch_paddr;
|
||||
assign fetch_rdata_o = instr_if_data_rdata_i;
|
||||
// instruction error
|
||||
logic ierr_valid_q, ierr_valid_n;
|
||||
// instruction error valid signal and exception, delayed one cycle
|
||||
logic ierr_valid_q, ierr_valid_n;
|
||||
exception fetch_ex_q, fetch_ex_n;
|
||||
|
||||
logic iaccess_err;
|
||||
logic ptw_active;
|
||||
logic walking_instr;
|
||||
|
@ -191,77 +196,75 @@ module mmu #(
|
|||
|
||||
assign itlb_lu_access = fetch_req_i;
|
||||
assign dtlb_lu_access = lsu_req_i;
|
||||
assign iaccess_err = fetch_req_i & (((priv_lvl_i == PRIV_LVL_U) && ~itlb_content.u)
|
||||
|| (sum_i && (priv_lvl_i == PRIV_LVL_S) && itlb_content.u));
|
||||
// Check whether we are allowed to access this memory region from a fetch perspective
|
||||
assign iaccess_err = fetch_req_i && (((priv_lvl_i == PRIV_LVL_U) && ~itlb_content.u)
|
||||
|| ((priv_lvl_i == PRIV_LVL_S) && itlb_content.u));
|
||||
|
||||
//-----------------------
|
||||
// Instruction interface
|
||||
// Instruction Interface
|
||||
//-----------------------
|
||||
// This is a full memory interface, e.g.: it handles all signals to the I$
|
||||
// Exceptions are always signaled together with the fetch_valid_o signal
|
||||
always_comb begin : instr_interface
|
||||
// MMU disabled: just pass through
|
||||
automatic logic fetch_valid = instr_if_data_rvalid_i;
|
||||
instr_if_data_req_o = fetch_req_i;
|
||||
fetch_paddr = fetch_vaddr_i;
|
||||
fetch_paddr = fetch_vaddr_i; // play through in case we disabled address translation
|
||||
fetch_gnt_o = instr_if_data_gnt_i;
|
||||
// two potential error sources:
|
||||
// 1. HPTW threw an exception -> signal with a page fault exception
|
||||
// 2. We got an access error because of insufficient permissions -> throw an access exception
|
||||
fetch_ex_n = '0;
|
||||
ierr_valid_n = 1'b0;
|
||||
|
||||
// MMU enabled: address from TLB, request delayed until hit. Error when TLB
|
||||
// hit and no access right or TLB hit and translated address not valid (e.g.
|
||||
// AXI decode error), or when PTW performs walk due to itlb miss and raises
|
||||
// AXI decode error), or when PTW performs walk due to ITLB miss and raises
|
||||
// an error.
|
||||
if (enable_translation_i) begin
|
||||
instr_if_data_req_o = 1'b0;
|
||||
/* verilator lint_off WIDTH */
|
||||
fetch_paddr = {itlb_content.ppn, fetch_vaddr_i[11:0]};
|
||||
/* verilator lint_on WIDTH */
|
||||
// 4K page
|
||||
fetch_paddr = {itlb_content.ppn, fetch_vaddr_i[11:0]};
|
||||
// this is a mega page
|
||||
if (itlb_is_2M) begin
|
||||
fetch_paddr[20:12] = fetch_vaddr_i[20:12];
|
||||
end
|
||||
|
||||
// this is a giga page
|
||||
if (itlb_is_1G) begin
|
||||
fetch_paddr[29:12] = fetch_vaddr_i[29:12];
|
||||
end
|
||||
|
||||
fetch_gnt_o = instr_if_data_gnt_i;
|
||||
|
||||
// TODO the following two ifs should be mutually exclusive
|
||||
// ---------
|
||||
// ITLB Hit
|
||||
// --------
|
||||
// if we hit the ITLB output the request signal immediately
|
||||
if (itlb_lu_hit) begin
|
||||
instr_if_data_req_o = fetch_req_i;
|
||||
|
||||
// we got an access error
|
||||
if (iaccess_err) begin
|
||||
// Play through an instruction fetch with error signaled with rvalid
|
||||
instr_if_data_req_o = 1'b0;
|
||||
fetch_gnt_o = 1'b1; // immediate grant
|
||||
//fetch_valid = 1'b0; NOTE: valid from previous fetch: pass through
|
||||
// NOTE: back-to-back transfers: We only get a request if previous
|
||||
// transfer is completed, or completes in this cycle)
|
||||
ierr_valid_n = 1'b1; // valid signaled in next cycle
|
||||
// immediately grant a fetch which threw an exception, and stop the request from happening
|
||||
instr_if_data_req_o = 1'b0;
|
||||
fetch_gnt_o = 1'b1;
|
||||
ierr_valid_n = 1'b1;
|
||||
// throw a page fault
|
||||
fetch_ex_n = {INSTR_PAGE_FAULT, fetch_vaddr_i, 1'b1};
|
||||
end
|
||||
end
|
||||
// ---------
|
||||
// ITLB Miss
|
||||
// ---------
|
||||
// watch out for exceptions happening during walking the page table
|
||||
if (ptw_active && walking_instr) begin
|
||||
// On error pass through fetch with error signaled with valid
|
||||
// on an error pass through fetch with an error signaled
|
||||
fetch_gnt_o = ptw_error;
|
||||
ierr_valid_n = ptw_error; // signal valid/error on next cycle
|
||||
fetch_ex_n = {INSTR_ACCESS_FAULT, fetch_vaddr_i, 1'b1};
|
||||
end
|
||||
end
|
||||
fetch_valid_o = fetch_valid || ierr_valid_q;
|
||||
// the fetch is valid if we either got an error in the previous cycle or the I$ gave us a valid signal.
|
||||
fetch_valid_o = instr_if_data_rvalid_i || ierr_valid_q;
|
||||
end
|
||||
// ----------------------------
|
||||
// Instruction Fetch Exception
|
||||
// ----------------------------
|
||||
always_comb begin : fetch_exception
|
||||
fetch_ex_o = '{default: 0};
|
||||
end
|
||||
|
||||
// registers
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if(~rst_ni) begin
|
||||
ierr_valid_q <= 1'b0;
|
||||
end else begin
|
||||
ierr_valid_q <= ierr_valid_n;
|
||||
end
|
||||
end
|
||||
|
||||
//-----------------------
|
||||
// Data interface
|
||||
//-----------------------
|
||||
|
@ -276,5 +279,16 @@ module mmu #(
|
|||
// TODO: Assign access exception
|
||||
lsu_exception_o = misaligned_ex_i;
|
||||
end
|
||||
|
||||
// ----------
|
||||
// Registers
|
||||
// ----------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if(~rst_ni) begin
|
||||
ierr_valid_q <= 1'b0;
|
||||
fetch_ex_q <= '0;
|
||||
end else begin
|
||||
ierr_valid_q <= ierr_valid_n;
|
||||
fetch_ex_q <= fetch_ex_n;
|
||||
end
|
||||
end
|
||||
endmodule
|
49
src/ptw.sv
49
src/ptw.sv
|
@ -24,16 +24,17 @@ module ptw #(
|
|||
parameter int ASID_WIDTH = 1
|
||||
)
|
||||
(
|
||||
input logic clk_i, // Clock
|
||||
input logic rst_ni, // Asynchronous reset active low
|
||||
input logic flush_i, // flush everything, we need to do this because
|
||||
// actually everything we do is speculative at this stage
|
||||
// e.g.: there could be a CSR instruction that changes everything
|
||||
input logic clk_i, // Clock
|
||||
input logic rst_ni, // Asynchronous reset active low
|
||||
input logic flush_i, // flush everything, we need to do this because
|
||||
// actually everything we do is speculative at this stage
|
||||
// e.g.: there could be a CSR instruction that changes everything
|
||||
output logic ptw_active_o,
|
||||
output logic walking_instr_o, // set when walking for TLB
|
||||
output logic ptw_error_o, // set when an error occured
|
||||
input logic enable_translation_i,
|
||||
// memory port
|
||||
output logic walking_instr_o, // set when walking for TLB
|
||||
output logic ptw_error_o, // set when an error occurred
|
||||
input logic enable_translation_i, // CSRs indicate to enable SV39
|
||||
input logic lsu_is_store_i, // this translation was triggered by a store
|
||||
// PTW Memory Port
|
||||
output logic [11:0] address_index_o,
|
||||
output logic [43:0] address_tag_o,
|
||||
output logic [63:0] data_wdata_o,
|
||||
|
@ -193,7 +194,6 @@ module ptw #(
|
|||
tag_valid_n = 1'b1;
|
||||
ptw_state_n = PTW_PTE_LOOKUP;
|
||||
end
|
||||
// we could potentially error out here
|
||||
end
|
||||
|
||||
PTW_PTE_LOOKUP: begin
|
||||
|
@ -228,9 +228,9 @@ module ptw #(
|
|||
// Update ITLB
|
||||
// ------------
|
||||
// If page is not executable, we can directly raise an error. This
|
||||
// saves the 'x' bits in the ITLB otherwise needed for access
|
||||
// right checks and doesn't put a useless entry into the TLB.
|
||||
if (~pte.x)
|
||||
// doesn't put a useless entry into the TLB. The same idea applies
|
||||
// to the access flag since we let the access flag be managed by SW.
|
||||
if (!pte.x || !pte.a)
|
||||
ptw_state_n = PTW_PROPAGATE_ERROR;
|
||||
else
|
||||
itlb_update_o = 1'b1;
|
||||
|
@ -239,15 +239,22 @@ module ptw #(
|
|||
// ------------
|
||||
// Update DTLB
|
||||
// ------------
|
||||
// If page is not readable (there are no write-only pages), or the
|
||||
// access that triggered the PTW is a write, but the page is not
|
||||
// write-able, we can directly raise an error. This saves the 'r'
|
||||
// bits in the TLB otherwise needed for access right checks and
|
||||
// doesn't put a useless entry into the TLB.
|
||||
if ((~pte.r && ~(pte.x && mxr_i)) || (~pte.w)) begin
|
||||
ptw_state_n = PTW_PROPAGATE_ERROR;
|
||||
end else begin
|
||||
// Check if the access flag has been set, otherwise throw a page-fault
|
||||
// and let the software handle those bits.
|
||||
// If page is not readable (there are no write-only pages)
|
||||
// we can directly raise an error. This doesn't put a useless
|
||||
// entry into the TLB.
|
||||
if (pte.a && (pte.r || (pte.x && mxr_i))) begin
|
||||
dtlb_update_o = 1'b1;
|
||||
end else begin
|
||||
ptw_state_n = PTW_PROPAGATE_ERROR;
|
||||
end
|
||||
// Request is a store: perform some additional checks
|
||||
// If the request was a store and the page is not write-able, raise an error
|
||||
// the same applies if the dirty flag is not set
|
||||
if (lsu_is_store_i && (!pte.w || !pte.d)) begin
|
||||
dtlb_update_o = 1'b0;
|
||||
ptw_state_n = PTW_PROPAGATE_ERROR;
|
||||
end
|
||||
end
|
||||
// this is a pointer to the next TLB level
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue