diff --git a/src/ariane.sv b/src/ariane.sv index 1c05ae5ee..b871d0d31 100644 --- a/src/ariane.sv +++ b/src/ariane.sv @@ -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 ), diff --git a/src/csr_regfile.sv b/src/csr_regfile.sv index 19b0b5069..1d19ecb41 100644 --- a/src/csr_regfile.sv +++ b/src/csr_regfile.sv @@ -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 // ---------------- diff --git a/src/ex_stage.sv b/src/ex_stage.sv index 875d01852..25aec75e2 100644 --- a/src/ex_stage.sv +++ b/src/ex_stage.sv @@ -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, diff --git a/src/lsu.sv b/src/lsu.sv index 03930aebe..e719f2c68 100644 --- a/src/lsu.sv +++ b/src/lsu.sv @@ -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 ), diff --git a/src/mmu.sv b/src/mmu.sv index e0143705e..7a3bc0036 100644 --- a/src/mmu.sv +++ b/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 \ No newline at end of file diff --git a/src/ptw.sv b/src/ptw.sv index e39f4a580..4e7fcd7dd 100644 --- a/src/ptw.sv +++ b/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