From 6aa098018683c0959f9d27e9582f0e805fa7a5b6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 20 Nov 2023 12:47:57 +0100 Subject: [PATCH 01/64] create mmu_unify folder --- core/mmu_unify/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 core/mmu_unify/README.md diff --git a/core/mmu_unify/README.md b/core/mmu_unify/README.md new file mode 100644 index 0000000000..43a6523f6d --- /dev/null +++ b/core/mmu_unify/README.md @@ -0,0 +1 @@ +Unification of MMUs: sv32, sv39 and sv39x4 \ No newline at end of file From 7b6d59f018e0106d9df7aff521825c00585eb8c6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 21 Nov 2023 16:55:08 +0100 Subject: [PATCH 02/64] first version of unified TLB --- core/mmu_unify/cva6_tlb.sv | 318 +++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 core/mmu_unify/cva6_tlb.sv diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv new file mode 100644 index 0000000000..e0cab24fb1 --- /dev/null +++ b/core/mmu_unify/cva6_tlb.sv @@ -0,0 +1,318 @@ +// Copyright (c) 2021 Thales. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Angela Gonzalez PlanV Technology +// Date: 20/11/2023 +// +// Description: Translation Lookaside Buffer, parameterizable to Sv32 or Sv39 , +// fully set-associative +// This module is an merge of the Sv32 TLB developed by Sebastien +// Jacq (Thales Research & Technology) and the Sv39 TLB developed +// by Florian Zaruba and David Schaffenrath to the Sv32 standard. +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2023-11-20 0.1 A.Gonzalez Generic TLB for CVA6 +// =========================================================================== // + +module cva6_tlb +import ariane_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // Flush signal + // Update TLB + input tlb_update_cva6_t update_i, + // Lookup signals + input logic lu_access_i, + input logic [ASID_WIDTH-1:0] lu_asid_i, + input logic [riscv::VLEN-1:0] lu_vaddr_i, + output riscv::pte_cva6_t lu_content_o, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + output logic [PT_LEVELS-2:0] lu_is_page_o, + output logic lu_hit_o +); + + +struct packed { + logic [ASID_LEN-1:0] asid; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; + logic valid; +} [TLB_ENTRIES-1:0] + tags_q, tags_n; + +riscv::pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; +// logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; +logic [PT_LEVELS-1:0] vpn_match; +logic [PT_LEVELS-1:0] page_match; +logic [PT_LEVELS-1:0] level_match; +logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic +logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy +//------------- +// Translation +//------------- +always_comb begin : translation + + + + // default assignment + lu_hit = '{default: 0}; + lu_hit_o = 1'b0; + lu_content_o = '{default: 0}; + lu_is_page_o = 0; + + + + for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin + + //at level 0 make page match always 1 + page_match = (tags_q[i].is_page)*2 +1; + + //build level match vector according to vpn_match and page_match + //a level has a match if all vpn of higher levels and current have a match, + //AND the page_match is also set + //At level 0 the page match is also set, so this level will have a match + //if all vpn levels match + for (int unsigned x = 0; x < PT_LEVELS; x++) begin + vpn_match[x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; == tags_q[i].vpn[x]; + level_match[x] = &vpn_match[PT_LEVELS-1:x] & page_match[x]; + end + + + + // first level match, check the ASID flags as well + // if the entry is associated to a global address, don't match the ASID (ASID is don't care) + if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g) ) begin + for (int unsigned a = PT_LEVELS-1; a>=0;a--) begin + //find highest level to have a match and break loop + if (level_match[a]) begin + lu_is_page_o[a] = tags_q[i].is_page[a]; + lu_content_o = content_q[i]; + lu_hit_o = 1'b1; + lu_hit[i] = 1'b1; + break; + end + end + + end + end +end + +logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high +logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high +logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_vpn_match; +logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_page_match; +logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_level_match; + + + +assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i); +assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i); + +// ------------------ +// Update and Flush +// ------------------ +always_comb begin : update_flush + tags_n = tags_q; + content_n = content_q; + + for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin + + //at level 0 make page match always 1 + vaddr_page_match = (tags_q[i].is_page)*2 +1; + + //build vaddr_level_match vector according to vaddr_vpn_match and vaddr_page_match + //a level has a match if all vpn of higher levels and current have a match, + //AND the page_match is also set + //At level 0 the vaddr_page_match is also set, so this level will have a match + //if all vaddr vpn levels match + for (int unsigned x = 0; x < PT_LEVELS; x++) begin + vaddr_vpn_match[x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[x].vpn; + level_match[x] = &vaddr_vpn_match[PT_LEVELS-1:x] & vaddr_page_match[x]; + end + + if (flush_i) begin + // invalidate logic + // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) + if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0; + // flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages + else if (asid_to_be_flushed_is0 && (level_match!=0) && (~vaddr_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) + else if ((!content_q[i].g) && (level_match!=0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) + else if ((!content_q[i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!asid_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // normal replacement + end else if (update_i.valid & replace_en[i]) begin + // update tag array + tags_n[i] = '{ + asid: update_i.asid, + vpn : update_i.vpn, + is_page: update_i.is_page, + valid: 1'b1 + }; + // and content as well + content_n[i] = update_i.content; + end + end +end + +// ----------------------------------------------- +// PLRU - Pseudo Least Recently Used Replacement +// ----------------------------------------------- +logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n; +logic en; +int unsigned idx_base, shift, new_index; +always_comb begin : plru_replacement + plru_tree_n = plru_tree_q; + en = '0; + idx_base = '0; + shift = '0; + new_index = '0; + // The PLRU-tree indexing: + // lvl0 0 + // / \ + // / \ + // lvl1 1 2 + // / \ / \ + // lvl2 3 4 5 6 + // / \ /\/\ /\ + // ... ... ... ... + // Just predefine which nodes will be set/cleared + // E.g. for a TLB with 8 entries, the for-loop is semantically + // equivalent to the following pseudo-code: + // unique case (1'b1) + // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1}; + // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0}; + // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1}; + // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0}; + // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1}; + // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0}; + // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1}; + // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0}; + // default: begin /* No hit */ end + // endcase + for ( + int unsigned i = 0; i < TLB_ENTRIES; i++ + ) begin + // we got a hit so update the pointer as it was least recently used + if (lu_hit[i] & lu_access_i) begin + // Set the nodes to the values we would expect + for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin + idx_base = $unsigned((2 ** lvl) - 1); + // lvl0 <=> MSB, lvl1 <=> MSB-1, ... + shift = $clog2(TLB_ENTRIES) - lvl; + // to circumvent the 32 bit integer arithmetic assignment + new_index = ~((i >> (shift - 1)) & 32'b1); + plru_tree_n[idx_base+(i>>shift)] = new_index[0]; + end + end + end + // Decode tree to write enable signals + // Next for-loop basically creates the following logic for e.g. an 8 entry + // TLB (note: pseudo-code obviously): + // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1} + // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0} + // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1} + // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0} + // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1} + // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0} + // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1} + // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0} + // For each entry traverse the tree. If every tree-node matches, + // the corresponding bit of the entry's index, this is + // the next entry to replace. + for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin + en = 1'b1; + for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin + idx_base = $unsigned((2 ** lvl) - 1); + // lvl0 <=> MSB, lvl1 <=> MSB-1, ... + shift = $clog2(TLB_ENTRIES) - lvl; + + // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1); + new_index = (i >> (shift - 1)) & 32'b1; + if (new_index[0]) begin + en &= plru_tree_q[idx_base+(i>>shift)]; + end else begin + en &= ~plru_tree_q[idx_base+(i>>shift)]; + end + end + replace_en[i] = en; + end +end + +// sequential process +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + tags_q <= '{default: 0}; + content_q <= '{default: 0}; + plru_tree_q <= '{default: 0}; + end else begin + tags_q <= tags_n; + content_q <= content_n; + plru_tree_q <= plru_tree_n; + end +end +//-------------- +// Sanity checks +//-------------- + +//pragma translate_off +`ifndef VERILATOR + +initial begin : p_assertions + assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1)) + else begin + $error("TLB size must be a multiple of 2 and greater than 1"); + $stop(); + end + assert (ASID_WIDTH >= 1) + else begin + $error("ASID width must be at least 1"); + $stop(); + end +end + +// Just for checking +function int countSetBits(logic [TLB_ENTRIES-1:0] vector); + automatic int count = 0; + foreach (vector[idx]) begin + count += vector[idx]; + end + return count; +endfunction + +assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1)) +else begin + $error("More then one hit in TLB!"); + $stop(); +end +assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1)) +else begin + $error("More then one TLB entry selected for next replace!"); + $stop(); +end + +`endif +//pragma translate_on + +endmodule From 5a4a7147efe262d1ae5e105a4838101873411769 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 21 Nov 2023 17:25:57 +0100 Subject: [PATCH 03/64] update data types related to unified mmu --- core/include/ariane_pkg.sv | 1896 ++++++++++++++++++------------------ core/include/riscv_pkg.sv | 1654 +++++++++++++++---------------- 2 files changed, 1779 insertions(+), 1771 deletions(-) diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index 446f609a61..2e7a47b632 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -19,989 +19,991 @@ // this is needed to propagate the // configuration in case Ariane is // instantiated in OpenPiton -`ifdef PITON_ARIANE -`include "l15.tmp.h" + `ifdef PITON_ARIANE + `include "l15.tmp.h" `endif -/// This package contains `functions` and global defines for CVA6. -/// *Note*: There are some parameters here as well which will eventually be -/// moved out to favour a fully parameterizable core. package ariane_pkg; - // TODO: Slowly move those parameters to the new system. - localparam NR_SB_ENTRIES = cva6_config_pkg::CVA6ConfigNrScoreboardEntries; // number of scoreboard entries - localparam TRANS_ID_BITS = $clog2( - NR_SB_ENTRIES - ); // depending on the number of scoreboard entries we need that many bits - // to uniquely identify the entry in the scoreboard - localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; - localparam BITS_SATURATION_COUNTER = 2; - - localparam ISSUE_WIDTH = 1; - - // depth of store-buffers, this needs to be a power of two - localparam logic [2:0] DEPTH_SPEC = 'd4; - - localparam int unsigned DCACHE_TYPE = int'(cva6_config_pkg::CVA6ConfigDcacheType); - // if DCACHE_TYPE = cva6_config_pkg::WT - // we can use a small commit queue since we have a write buffer in the dcache - // we could in principle do without the commit queue in this case, but the timing degrades if we do that due - // to longer paths into the commit stage - // if DCACHE_TYPE = cva6_config_pkg::WB - // allocate more space for the commit buffer to be on the save side, this needs to be a power of two - localparam logic [2:0] DEPTH_COMMIT = 'd4; - - localparam bit FPGA_EN = cva6_config_pkg::CVA6ConfigFPGAEn; // Is FPGA optimization of CV32A6 - - localparam bit RVC = cva6_config_pkg::CVA6ConfigCExtEn; // Is C extension configuration - - // Transprecision float unit - localparam int unsigned LAT_COMP_FP32 = 'd2; - localparam int unsigned LAT_COMP_FP64 = 'd3; - localparam int unsigned LAT_COMP_FP16 = 'd1; - localparam int unsigned LAT_COMP_FP16ALT = 'd1; - localparam int unsigned LAT_COMP_FP8 = 'd1; - localparam int unsigned LAT_DIVSQRT = 'd2; - localparam int unsigned LAT_NONCOMP = 'd1; - localparam int unsigned LAT_CONV = 'd2; - - localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN - 32{1'b0}}, 32'h0602}; - localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN - 32{1'b0}}, 32'd3}; - - // 32 registers - localparam REG_ADDR_SIZE = 5; - - // Read ports for general purpose register files - localparam NR_RGPR_PORTS = 2; - - // static debug hartinfo - // debug causes - localparam logic [2:0] CauseBreakpoint = 3'h1; - localparam logic [2:0] CauseTrigger = 3'h2; - localparam logic [2:0] CauseRequest = 3'h3; - localparam logic [2:0] CauseSingleStep = 3'h4; - // amount of data count registers implemented - localparam logic [3:0] DataCount = 4'h2; - - // address where data0-15 is shadowed or if shadowed in a CSR - // address of the first CSR used for shadowing the data - localparam logic [11:0] DataAddr = 12'h380; // we are aligned with Rocket here - typedef struct packed { - logic [31:24] zero1; - logic [23:20] nscratch; - logic [19:17] zero0; - logic dataaccess; - logic [15:12] datasize; - logic [11:0] dataaddr; - } hartinfo_t; - - localparam hartinfo_t DebugHartInfo = '{ - zero1: '0, - nscratch: 2, // Debug module needs at least two scratch regs - zero0: '0, - dataaccess: 1'b1, // data registers are memory mapped in the debugger - datasize: DataCount, - dataaddr: DataAddr - }; - - // enables a commit log which matches spikes commit log format for easier trace comparison - localparam bit ENABLE_SPIKE_COMMIT_LOG = 1'b1; - - // ------------- Dangerous ------------- - // if set to zero a flush will not invalidate the cache-lines, in a single core environment - // where coherence is not necessary this can improve performance. This needs to be switched on - // when more than one core is in a system - localparam logic INVALIDATE_ON_FLUSH = 1'b1; + // --------------- + // Global Config + // --------------- + // This is the new user config interface system. If you need to parameterize something + // within Ariane add a field here and assign a default value to the config. Please make + // sure to add a propper parameter check to the `check_cfg` function. + typedef struct packed { + int unsigned dummy; + } cva6_cfg_t; + + localparam cva6_cfg_t cva6_cfg_empty = { + 32'b0 + }; + + localparam NrMaxRules = 16; + typedef struct packed { + int RASDepth; + int BTBEntries; + int BHTEntries; + // PMAs + int unsigned NrNonIdempotentRules; // Number of non idempotent rules + logic [NrMaxRules-1:0][63:0] NonIdempotentAddrBase; // base which needs to match + logic [NrMaxRules-1:0][63:0] NonIdempotentLength; // bit mask which bits to consider when matching the rule + int unsigned NrExecuteRegionRules; // Number of regions which have execute property + logic [NrMaxRules-1:0][63:0] ExecuteRegionAddrBase; // base which needs to match + logic [NrMaxRules-1:0][63:0] ExecuteRegionLength; // bit mask which bits to consider when matching the rule + int unsigned NrCachedRegionRules; // Number of regions which have cached property + logic [NrMaxRules-1:0][63:0] CachedRegionAddrBase; // base which needs to match + logic [NrMaxRules-1:0][63:0] CachedRegionLength; // bit mask which bits to consider when matching the rule + // cache config + bit AxiCompliant; // set to 1 when using in conjunction with 64bit AXI bus adapter + bit SwapEndianess; // set to 1 to swap endianess inside L1.5 openpiton adapter + // + logic [63:0] DmBaseAddress; // offset of the debug module + int unsigned NrPMPEntries; // Number of PMP entries + } ariane_cfg_t; + + localparam ariane_cfg_t ArianeDefaultConfig = '{ + RASDepth: int'(cva6_config_pkg::CVA6ConfigRASDepth), + BTBEntries: int'(cva6_config_pkg::CVA6ConfigBTBEntries), + BHTEntries: int'(cva6_config_pkg::CVA6ConfigBHTEntries), + // idempotent region + NrNonIdempotentRules: unsigned'(2), + NonIdempotentAddrBase: 1024'({64'b0, 64'b0}), + NonIdempotentLength: 1024'({64'b0, 64'b0}), + NrExecuteRegionRules: unsigned'(3), + // DRAM, Boot ROM, Debug Module + ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), + ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), + // cached region + NrCachedRegionRules: unsigned'(1), + CachedRegionAddrBase: 1024'({64'h8000_0000}), + CachedRegionLength: 1024'({64'h40000000}), + // cache config + AxiCompliant: 1'b1, + SwapEndianess: 1'b0, + // debug + DmBaseAddress: 64'h0, + NrPMPEntries: unsigned'(cva6_config_pkg::CVA6ConfigNrPMPEntries) + }; + + // Function being called to check parameters + function automatic void check_cfg (ariane_cfg_t Cfg); + // pragma translate_off + `ifndef VERILATOR + assert(Cfg.RASDepth > 0); + assert(2**$clog2(Cfg.BTBEntries) == Cfg.BTBEntries); + assert(2**$clog2(Cfg.BHTEntries) == Cfg.BHTEntries); + assert(Cfg.NrNonIdempotentRules <= NrMaxRules); + assert(Cfg.NrExecuteRegionRules <= NrMaxRules); + assert(Cfg.NrCachedRegionRules <= NrMaxRules); + assert(Cfg.NrPMPEntries <= 16); + `endif + // pragma translate_on + endfunction + + function automatic logic range_check(logic[63:0] base, logic[63:0] len, logic[63:0] address); + // if len is a power of two, and base is properly aligned, this check could be simplified + // Extend base by one bit to prevent an overflow. + return (address >= base) && (({1'b0, address}) < (65'(base)+len)); + endfunction : range_check + + function automatic logic is_inside_nonidempotent_regions (ariane_cfg_t Cfg, logic[63:0] address); + logic[NrMaxRules-1:0] pass; + pass = '0; + for (int unsigned k = 0; k < Cfg.NrNonIdempotentRules; k++) begin + pass[k] = range_check(Cfg.NonIdempotentAddrBase[k], Cfg.NonIdempotentLength[k], address); + end + return |pass; + endfunction : is_inside_nonidempotent_regions + + function automatic logic is_inside_execute_regions (ariane_cfg_t Cfg, logic[63:0] address); + // if we don't specify any region we assume everything is accessible + logic[NrMaxRules-1:0] pass; + pass = '0; + for (int unsigned k = 0; k < Cfg.NrExecuteRegionRules; k++) begin + pass[k] = range_check(Cfg.ExecuteRegionAddrBase[k], Cfg.ExecuteRegionLength[k], address); + end + return |pass; + endfunction : is_inside_execute_regions + + function automatic logic is_inside_cacheable_regions (ariane_cfg_t Cfg, logic[63:0] address); + automatic logic[NrMaxRules-1:0] pass; + pass = '0; + for (int unsigned k = 0; k < Cfg.NrCachedRegionRules; k++) begin + pass[k] = range_check(Cfg.CachedRegionAddrBase[k], Cfg.CachedRegionLength[k], address); + end + return |pass; + endfunction : is_inside_cacheable_regions + + // TODO: Slowly move those parameters to the new system. + localparam NR_SB_ENTRIES = cva6_config_pkg::CVA6ConfigNrScoreboardEntries; // number of scoreboard entries + localparam TRANS_ID_BITS = $clog2(NR_SB_ENTRIES); // depending on the number of scoreboard entries we need that many bits + // to uniquely identify the entry in the scoreboard + localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; + localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; + localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; + localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + localparam BITS_SATURATION_COUNTER = 2; + localparam NR_COMMIT_PORTS = cva6_config_pkg::CVA6ConfigNrCommitPorts; + + localparam ENABLE_RENAME = cva6_config_pkg::CVA6ConfigRenameEn; + + localparam ISSUE_WIDTH = 1; + + // depth of store-buffers, this needs to be a power of two + localparam int unsigned DEPTH_SPEC = 4; + + localparam int unsigned DCACHE_TYPE = int'(cva6_config_pkg::CVA6ConfigDcacheType); + // if DCACHE_TYPE = cva6_config_pkg::WT + // we can use a small commit queue since we have a write buffer in the dcache + // we could in principle do without the commit queue in this case, but the timing degrades if we do that due + // to longer paths into the commit stage + // if DCACHE_TYPE = cva6_config_pkg::WB + // allocate more space for the commit buffer to be on the save side, this needs to be a power of two + localparam int unsigned DEPTH_COMMIT = (DCACHE_TYPE == int'(cva6_config_pkg::WT)) ? 4 : 8; + + localparam bit FPGA_EN = cva6_config_pkg::CVA6ConfigFPGAEn; // Is FPGA optimization of CV32A6 + + localparam bit RVC = cva6_config_pkg::CVA6ConfigCExtEn; // Is C extension configuration +`ifdef PITON_ARIANE + // Floating-point extensions configuration + localparam bit RVF = riscv::IS_XLEN64; // Is F extension enabled + localparam bit RVD = riscv::IS_XLEN64; // Is D extension enabled +`else + // Floating-point extensions configuration + localparam bit RVF = (riscv::IS_XLEN64 | riscv::IS_XLEN32) & riscv::FPU_EN; // Is F extension enabled for both 32 Bit and 64 bit CPU + localparam bit RVD = (riscv::IS_XLEN64 ? 1:0) & riscv::FPU_EN; // Is D extension enabled for only 64 bit CPU +`endif + localparam bit RVA = cva6_config_pkg::CVA6ConfigAExtEn; // Is A extension enabled + localparam bit RVV = cva6_config_pkg::CVA6ConfigVExtEn; + + // Is the accelerator enabled? + localparam bit ENABLE_ACCELERATOR = RVV; // Currently only used by V extension (Ara) + + // Transprecision floating-point extensions configuration + localparam bit XF16 = cva6_config_pkg::CVA6ConfigF16En | RVV; // Is half-precision float extension (Xf16) enabled + localparam bit XF16ALT = cva6_config_pkg::CVA6ConfigF16AltEn; // Is alternative half-precision float extension (Xf16alt) enabled + localparam bit XF8 = cva6_config_pkg::CVA6ConfigF8En; // Is quarter-precision float extension (Xf8) enabled + localparam bit XFVEC = cva6_config_pkg::CVA6ConfigFVecEn; // Is vectorial float extension (Xfvec) enabled + + // Transprecision float unit + localparam int unsigned LAT_COMP_FP32 = 'd2; + localparam int unsigned LAT_COMP_FP64 = 'd3; + localparam int unsigned LAT_COMP_FP16 = 'd1; + localparam int unsigned LAT_COMP_FP16ALT = 'd1; + localparam int unsigned LAT_COMP_FP8 = 'd1; + localparam int unsigned LAT_DIVSQRT = 'd2; + localparam int unsigned LAT_NONCOMP = 'd1; + localparam int unsigned LAT_CONV = 'd2; + + // -------------------------------------- + // vvvv Don't change these by hand! vvvv + localparam bit FP_PRESENT = RVF | RVD | XF16 | XF16ALT | XF8; + + // Length of widest floating-point format + localparam FLEN = RVD ? 64 : // D ext. + RVF ? 32 : // F ext. + XF16 ? 16 : // Xf16 ext. + XF16ALT ? 16 : // Xf16alt ext. + XF8 ? 8 : // Xf8 ext. + 1; // Unused in case of no FP + + localparam bit NSX = XF16 | XF16ALT | XF8 | XFVEC; // Are non-standard extensions present? + + localparam bit RVFVEC = RVF & XFVEC & FLEN>32; // FP32 vectors available if vectors and larger fmt enabled + localparam bit XF16VEC = XF16 & XFVEC & FLEN>16; // FP16 vectors available if vectors and larger fmt enabled + localparam bit XF16ALTVEC = XF16ALT & XFVEC & FLEN>16; // FP16ALT vectors available if vectors and larger fmt enabled + localparam bit XF8VEC = XF8 & XFVEC & FLEN>8; // FP8 vectors available if vectors and larger fmt enabled + // ^^^^ until here ^^^^ + // --------------------- + + localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN-32{1'b0}}, 32'h0602}; + localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN-32{1'b0}}, 32'd3}; + + localparam riscv::xlen_t ISA_CODE = (riscv::XLEN'(RVA) << 0) // A - Atomic Instructions extension + | (riscv::XLEN'(RVC) << 2) // C - Compressed extension + | (riscv::XLEN'(RVD) << 3) // D - Double precsision floating-point extension + | (riscv::XLEN'(RVF) << 5) // F - Single precsision floating-point extension + | (riscv::XLEN'(1 ) << 8) // I - RV32I/64I/128I base ISA + | (riscv::XLEN'(1 ) << 12) // M - Integer Multiply/Divide extension + | (riscv::XLEN'(0 ) << 13) // N - User level interrupts supported + | (riscv::XLEN'(1 ) << 18) // S - Supervisor mode implemented + | (riscv::XLEN'(1 ) << 20) // U - User mode implemented + | (riscv::XLEN'(RVV) << 21) // V - Vector extension + | (riscv::XLEN'(NSX) << 23) // X - Non-standard extensions present + | ((riscv::XLEN == 64 ? 2 : 1) << riscv::XLEN-2); // MXL + + // 32 registers + 1 bit for re-naming = 6 + localparam REG_ADDR_SIZE = 6; + + localparam bit CVXIF_PRESENT = cva6_config_pkg::CVA6ConfigCvxifEn; + + // when cvx interface or the accelerator port is present, use an additional writeback port + localparam NR_WB_PORTS = (CVXIF_PRESENT || ENABLE_ACCELERATOR) ? 5 : 4; + + // Read ports for general purpose register files + localparam NR_RGPR_PORTS = 2; + typedef logic [(NR_RGPR_PORTS == 3 ? riscv::XLEN : FLEN)-1:0] rs3_len_t; + + // static debug hartinfo + localparam ariane_dm_pkg::hartinfo_t DebugHartInfo = '{ + zero1: '0, + nscratch: 2, // Debug module needs at least two scratch regs + zero0: '0, + dataaccess: 1'b1, // data registers are memory mapped in the debugger + datasize: ariane_dm_pkg::DataCount, + dataaddr: ariane_dm_pkg::DataAddr + }; + + // enables a commit log which matches spikes commit log format for easier trace comparison + localparam bit ENABLE_SPIKE_COMMIT_LOG = 1'b1; + + // ------------- Dangerouse ------------- + // if set to zero a flush will not invalidate the cache-lines, in a single core environment + // where coherence is not necessary this can improve performance. This needs to be switched on + // when more than one core is in a system + localparam logic INVALIDATE_ON_FLUSH = 1'b1; `ifdef SPIKE_TANDEM - // Spike still places 0 in TVAL for ENV_CALL_* exceptions. - // This may eventually go away when Spike starts to handle TVAL for *all* exceptions. - localparam bit ZERO_TVAL = 1'b1; + // enable performance cycle counter, if set to zero mcycle will be incremented + // with instret (non RISC-V conformal) + localparam bit ENABLE_CYCLE_COUNT = 1'b0; + // mark WIF as nop + localparam bit ENABLE_WFI = 1'b0; + // Spike zeros tval on all exception except memory faults + localparam bit ZERO_TVAL = 1'b1; `else - localparam bit ZERO_TVAL = 1'b0; + localparam bit ENABLE_CYCLE_COUNT = 1'b1; + localparam bit ENABLE_WFI = 1'b1; + localparam bit ZERO_TVAL = 1'b0; `endif - // read mask for SSTATUS over MMSTATUS - localparam logic [63:0] SMODE_STATUS_READ_MASK = riscv::SSTATUS_UIE - | riscv::SSTATUS_SIE + // read mask for SSTATUS over MMSTATUS + localparam logic [63:0] SMODE_STATUS_READ_MASK = riscv::SSTATUS_UIE + | riscv::SSTATUS_SIE + | riscv::SSTATUS_SPIE + | riscv::SSTATUS_SPP + | riscv::SSTATUS_FS + | riscv::SSTATUS_XS + | riscv::SSTATUS_SUM + | riscv::SSTATUS_MXR + | riscv::SSTATUS_UPIE + | riscv::SSTATUS_SPIE + | riscv::SSTATUS_UXL + | riscv::SSTATUS_SD; + + localparam logic [63:0] SMODE_STATUS_WRITE_MASK = riscv::SSTATUS_SIE | riscv::SSTATUS_SPIE | riscv::SSTATUS_SPP | riscv::SSTATUS_FS - | riscv::SSTATUS_XS | riscv::SSTATUS_SUM - | riscv::SSTATUS_MXR - | riscv::SSTATUS_UPIE - | riscv::SSTATUS_SPIE - | riscv::SSTATUS_UXL - | riscv::SSTATUS_SD; - - localparam logic [63:0] SMODE_STATUS_WRITE_MASK = riscv::SSTATUS_SIE - | riscv::SSTATUS_SPIE - | riscv::SSTATUS_SPP - | riscv::SSTATUS_FS - | riscv::SSTATUS_SUM - | riscv::SSTATUS_MXR; - // --------------- - // AXI - // --------------- - - localparam FETCH_USER_WIDTH = cva6_config_pkg::CVA6ConfigFetchUserWidth; - localparam DATA_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; - localparam AXI_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn | cva6_config_pkg::CVA6ConfigFetchUserEn; - localparam AXI_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; - localparam DATA_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn; - localparam FETCH_USER_EN = cva6_config_pkg::CVA6ConfigFetchUserEn; - - typedef enum logic { - SINGLE_REQ, - CACHE_LINE_REQ - } ad_req_t; - - // --------------- - // Fetch Stage - // --------------- - - // leave as is (fails with >8 entries and wider fetch width) - localparam int unsigned FETCH_FIFO_DEPTH = 4; - localparam int unsigned FETCH_WIDTH = 32; - // maximum instructions we can fetch on one request (we support compressed instructions) - localparam int unsigned INSTR_PER_FETCH = RVC == 1'b1 ? (FETCH_WIDTH / 16) : 1; - localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2( - ariane_pkg::INSTR_PER_FETCH - ) : 1; - - // --------------- - // Enable BITMANIP - // --------------- - localparam bit BITMANIP = cva6_config_pkg::CVA6ConfigBExtEn; - - // Only use struct when signals have same direction - // exception - typedef struct packed { - riscv::xlen_t cause; // cause of exception - riscv::xlen_t tval; // additional information of causing exception (e.g.: instruction causing it), - // address of LD/ST fault - logic valid; - } exception_t; - - typedef enum logic [2:0] { - NoCF, // No control flow prediction - Branch, // Branch - Jump, // Jump to address from immediate - JumpR, // Jump to address from registers - Return // Return Address Prediction - } cf_t; - - // branch-predict - // this is the struct we get back from ex stage and we will use it to update - // all the necessary data structures - // bp_resolve_t - typedef struct packed { - logic valid; // prediction with all its values is valid - logic [riscv::VLEN-1:0] pc; // PC of predict or mis-predict - logic [riscv::VLEN-1:0] target_address; // target address at which to jump, or not - logic is_mispredict; // set if this was a mis-predict - logic is_taken; // branch is taken - cf_t cf_type; // Type of control flow change - } bp_resolve_t; - - // branchpredict scoreboard entry - // this is the struct which we will inject into the pipeline to guide the various - // units towards the correct branch decision and resolve - typedef struct packed { - cf_t cf; // type of control flow prediction - logic [riscv::VLEN-1:0] predict_address; // target address at which to jump, or not - } branchpredict_sbe_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] pc; // update at PC - logic [riscv::VLEN-1:0] target_address; - } btb_update_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] target_address; - } btb_prediction_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] ra; - } ras_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] pc; // update at PC - logic taken; - } bht_update_t; - - typedef struct packed { - logic valid; - logic taken; - } bht_prediction_t; - - typedef struct packed { - logic valid; - logic [1:0] saturation_counter; - } bht_t; - - typedef enum logic [3:0] { - NONE, // 0 - LOAD, // 1 - STORE, // 2 - ALU, // 3 - CTRL_FLOW, // 4 - MULT, // 5 - CSR, // 6 - FPU, // 7 - FPU_VEC, // 8 - CVXIF, // 9 - ACCEL // 10 - } fu_t; - - localparam EXC_OFF_RST = 8'h80; - - localparam SupervisorIrq = 1; - localparam MachineIrq = 0; - - // All information needed to determine whether we need to associate an interrupt - // with the corresponding instruction or not. - typedef struct packed { - riscv::xlen_t mie; - riscv::xlen_t mip; - riscv::xlen_t mideleg; - logic sie; - logic global_enable; - } irq_ctrl_t; - - // --------------- - // Cache config - // --------------- - - // for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h + | riscv::SSTATUS_MXR; + // --------------- + // AXI + // --------------- + + localparam AXI_ID_WIDTH = cva6_config_pkg::CVA6ConfigAxiIdWidth; + localparam AXI_ADDR_WIDTH = cva6_config_pkg::CVA6ConfigAxiAddrWidth; + localparam AXI_DATA_WIDTH = cva6_config_pkg::CVA6ConfigAxiDataWidth; + localparam FETCH_USER_WIDTH = cva6_config_pkg::CVA6ConfigFetchUserWidth; + localparam DATA_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; + localparam AXI_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn | cva6_config_pkg::CVA6ConfigFetchUserEn; + localparam AXI_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; + localparam DATA_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn; + localparam FETCH_USER_EN = cva6_config_pkg::CVA6ConfigFetchUserEn; + + // --------------- + // Fetch Stage + // --------------- + + // leave as is (fails with >8 entries and wider fetch width) + localparam int unsigned FETCH_FIFO_DEPTH = 4; + localparam int unsigned FETCH_WIDTH = 32; + // maximum instructions we can fetch on one request (we support compressed instructions) + localparam int unsigned INSTR_PER_FETCH = RVC == 1'b1 ? (FETCH_WIDTH / 16) : 1; + localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2(ariane_pkg::INSTR_PER_FETCH) : 1; + + // --------------- + // Enable BITMANIP + // --------------- + localparam bit BITMANIP = cva6_config_pkg::CVA6ConfigBExtEn; + + // Only use struct when signals have same direction + // exception + typedef struct packed { + riscv::xlen_t cause; // cause of exception + riscv::xlen_t tval; // additional information of causing exception (e.g.: instruction causing it), + // address of LD/ST fault + logic valid; + } exception_t; + + typedef enum logic [2:0] { + NoCF, // No control flow prediction + Branch, // Branch + Jump, // Jump to address from immediate + JumpR, // Jump to address from registers + Return // Return Address Prediction + } cf_t; + + // branch-predict + // this is the struct we get back from ex stage and we will use it to update + // all the necessary data structures + // bp_resolve_t + typedef struct packed { + logic valid; // prediction with all its values is valid + logic [riscv::VLEN-1:0] pc; // PC of predict or mis-predict + logic [riscv::VLEN-1:0] target_address; // target address at which to jump, or not + logic is_mispredict; // set if this was a mis-predict + logic is_taken; // branch is taken + cf_t cf_type; // Type of control flow change + } bp_resolve_t; + + // branchpredict scoreboard entry + // this is the struct which we will inject into the pipeline to guide the various + // units towards the correct branch decision and resolve + typedef struct packed { + cf_t cf; // type of control flow prediction + logic [riscv::VLEN-1:0] predict_address; // target address at which to jump, or not + } branchpredict_sbe_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] pc; // update at PC + logic [riscv::VLEN-1:0] target_address; + } btb_update_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] target_address; + } btb_prediction_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] ra; + } ras_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] pc; // update at PC + logic taken; + } bht_update_t; + + typedef struct packed { + logic valid; + logic taken; + } bht_prediction_t; + + typedef struct packed { + logic valid; + logic [1:0] saturation_counter; + } bht_t; + + typedef enum logic[3:0] { + NONE, // 0 + LOAD, // 1 + STORE, // 2 + ALU, // 3 + CTRL_FLOW, // 4 + MULT, // 5 + CSR, // 6 + FPU, // 7 + FPU_VEC, // 8 + CVXIF, // 9 + ACCEL // 10 + } fu_t; + + localparam EXC_OFF_RST = 8'h80; + + localparam SupervisorIrq = 1; + localparam MachineIrq = 0; + + // All information needed to determine whether we need to associate an interrupt + // with the corresponding instruction or not. + typedef struct packed { + riscv::xlen_t mie; + riscv::xlen_t mip; + riscv::xlen_t mideleg; + logic sie; + logic global_enable; + } irq_ctrl_t; + + // --------------- + // Cache config + // --------------- + +// for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h `ifdef PITON_ARIANE `ifndef CONFIG_L1I_CACHELINE_WIDTH - `define CONFIG_L1I_CACHELINE_WIDTH 128 + `define CONFIG_L1I_CACHELINE_WIDTH 128 `endif `ifndef CONFIG_L1I_ASSOCIATIVITY - `define CONFIG_L1I_ASSOCIATIVITY 4 + `define CONFIG_L1I_ASSOCIATIVITY 4 `endif `ifndef CONFIG_L1I_SIZE - `define CONFIG_L1I_SIZE 16*1024 + `define CONFIG_L1I_SIZE 16*1024 `endif `ifndef CONFIG_L1D_CACHELINE_WIDTH - `define CONFIG_L1D_CACHELINE_WIDTH 128 + `define CONFIG_L1D_CACHELINE_WIDTH 128 `endif `ifndef CONFIG_L1D_ASSOCIATIVITY - `define CONFIG_L1D_ASSOCIATIVITY 8 + `define CONFIG_L1D_ASSOCIATIVITY 8 `endif `ifndef CONFIG_L1D_SIZE - `define CONFIG_L1D_SIZE 32*1024 + `define CONFIG_L1D_SIZE 32*1024 `endif `ifndef L15_THREADID_WIDTH - `define L15_THREADID_WIDTH 3 + `define L15_THREADID_WIDTH 3 `endif - // I$ - localparam int unsigned ICACHE_LINE_WIDTH = `CONFIG_L1I_CACHELINE_WIDTH; - localparam int unsigned ICACHE_SET_ASSOC = `CONFIG_L1I_ASSOCIATIVITY; - localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(`CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); - localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; - localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit - // D$ - localparam int unsigned DCACHE_LINE_WIDTH = `CONFIG_L1D_CACHELINE_WIDTH; - localparam int unsigned DCACHE_SET_ASSOC = `CONFIG_L1D_ASSOCIATIVITY; - localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(`CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); - localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; - localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit - localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; - - localparam int unsigned MEM_TID_WIDTH = `L15_THREADID_WIDTH; + // I$ + localparam int unsigned ICACHE_LINE_WIDTH = `CONFIG_L1I_CACHELINE_WIDTH; + localparam int unsigned ICACHE_SET_ASSOC = `CONFIG_L1I_ASSOCIATIVITY; + localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(`CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); + localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; + localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit + // D$ + localparam int unsigned DCACHE_LINE_WIDTH = `CONFIG_L1D_CACHELINE_WIDTH; + localparam int unsigned DCACHE_SET_ASSOC = `CONFIG_L1D_ASSOCIATIVITY; + localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(`CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); + localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; + localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit + localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; + + localparam int unsigned MEM_TID_WIDTH = `L15_THREADID_WIDTH; `else - // I$ - localparam int unsigned CONFIG_L1I_SIZE = cva6_config_pkg::CVA6ConfigIcacheByteSize; // in byte - localparam int unsigned ICACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigIcacheSetAssoc; // number of ways - localparam int unsigned ICACHE_INDEX_WIDTH = $clog2( - CONFIG_L1I_SIZE / ICACHE_SET_ASSOC - ); // in bit, contains also offset width - localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; // in bit - localparam int unsigned ICACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit - localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit - // D$ - localparam int unsigned CONFIG_L1D_SIZE = cva6_config_pkg::CVA6ConfigDcacheByteSize; // in byte - localparam int unsigned DCACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigDcacheSetAssoc; // number of ways - localparam int unsigned DCACHE_INDEX_WIDTH = $clog2( - CONFIG_L1D_SIZE / DCACHE_SET_ASSOC - ); // in bit, contains also offset width - localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; // in bit - localparam int unsigned DCACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit - localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit - localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; - - localparam int unsigned MEM_TID_WIDTH = cva6_config_pkg::CVA6ConfigMemTidWidth; + // I$ + localparam int unsigned CONFIG_L1I_SIZE = cva6_config_pkg::CVA6ConfigIcacheByteSize; // in byte + localparam int unsigned ICACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigIcacheSetAssoc; // number of ways + localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); // in bit, contains also offset width + localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN-ICACHE_INDEX_WIDTH; // in bit + localparam int unsigned ICACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit + localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit + // D$ + localparam int unsigned CONFIG_L1D_SIZE = cva6_config_pkg::CVA6ConfigDcacheByteSize; // in byte + localparam int unsigned DCACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigDcacheSetAssoc; // number of ways + localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); // in bit, contains also offset width + localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN-DCACHE_INDEX_WIDTH; // in bit + localparam int unsigned DCACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit + localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit + localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; + + localparam int unsigned MEM_TID_WIDTH = cva6_config_pkg::CVA6ConfigMemTidWidth; `endif - localparam int unsigned DCACHE_TID_WIDTH = cva6_config_pkg::CVA6ConfigDcacheIdWidth; - - localparam int unsigned WT_DCACHE_WBUF_DEPTH = cva6_config_pkg::CVA6ConfigWtDcacheWbufDepth; - - // --------------- - // EX Stage - // --------------- - - typedef enum logic [7:0] { // basic ALU op - ADD, - SUB, - ADDW, - SUBW, - // logic operations - XORL, - ORL, - ANDL, - // shifts - SRA, - SRL, - SLL, - SRLW, - SLLW, - SRAW, - // comparisons - LTS, - LTU, - GES, - GEU, - EQ, - NE, - // jumps - JALR, - BRANCH, - // set lower than operations - SLTS, - SLTU, - // CSR functions - MRET, - SRET, - DRET, - ECALL, - WFI, - FENCE, - FENCE_I, - SFENCE_VMA, - CSR_WRITE, - CSR_READ, - CSR_SET, - CSR_CLEAR, - // LSU functions - LD, - SD, - LW, - LWU, - SW, - LH, - LHU, - SH, - LB, - SB, - LBU, - // Atomic Memory Operations - AMO_LRW, - AMO_LRD, - AMO_SCW, - AMO_SCD, - AMO_SWAPW, - AMO_ADDW, - AMO_ANDW, - AMO_ORW, - AMO_XORW, - AMO_MAXW, - AMO_MAXWU, - AMO_MINW, - AMO_MINWU, - AMO_SWAPD, - AMO_ADDD, - AMO_ANDD, - AMO_ORD, - AMO_XORD, - AMO_MAXD, - AMO_MAXDU, - AMO_MIND, - AMO_MINDU, - // Multiplications - MUL, - MULH, - MULHU, - MULHSU, - MULW, - // Divisions - DIV, - DIVU, - DIVW, - DIVUW, - REM, - REMU, - REMW, - REMUW, - // Floating-Point Load and Store Instructions - FLD, - FLW, - FLH, - FLB, - FSD, - FSW, - FSH, - FSB, - // Floating-Point Computational Instructions - FADD, - FSUB, - FMUL, - FDIV, - FMIN_MAX, - FSQRT, - FMADD, - FMSUB, - FNMSUB, - FNMADD, - // Floating-Point Conversion and Move Instructions - FCVT_F2I, - FCVT_I2F, - FCVT_F2F, - FSGNJ, - FMV_F2X, - FMV_X2F, - // Floating-Point Compare Instructions - FCMP, - // Floating-Point Classify Instruction - FCLASS, - // Vectorial Floating-Point Instructions that don't directly map onto the scalar ones - VFMIN, - VFMAX, - VFSGNJ, - VFSGNJN, - VFSGNJX, - VFEQ, - VFNE, - VFLT, - VFGE, - VFLE, - VFGT, - VFCPKAB_S, - VFCPKCD_S, - VFCPKAB_D, - VFCPKCD_D, - // Offload Instructions to be directed into cv_x_if - OFFLOAD, - // Or-Combine and REV8 - ORCB, - REV8, - // Bitwise Rotation - ROL, - ROLW, - ROR, - RORI, - RORIW, - RORW, - // Sign and Zero Extend - SEXTB, - SEXTH, - ZEXTH, - // Count population - CPOP, - CPOPW, - // Count Leading/Training Zeros - CLZ, - CLZW, - CTZ, - CTZW, - // Carry less multiplication Op's - CLMUL, - CLMULH, - CLMULR, - // Single bit instructions Op's - BCLR, - BCLRI, - BEXT, - BEXTI, - BINV, - BINVI, - BSET, - BSETI, - // Integer minimum/maximum - MAX, - MAXU, - MIN, - MINU, - // Shift with Add Unsigned Word and Unsigned Word Op's (Bitmanip) - SH1ADDUW, - SH2ADDUW, - SH3ADDUW, - ADDUW, - SLLIUW, - // Shift with Add (Bitmanip) - SH1ADD, - SH2ADD, - SH3ADD, - // Bitmanip Logical with negate op (Bitmanip) - ANDN, - ORN, - XNOR, - // Accelerator operations - ACCEL_OP, - ACCEL_OP_FS1, - ACCEL_OP_FD, - ACCEL_OP_LOAD, - ACCEL_OP_STORE, - // Zicond instruction - CZERO_EQZ, - CZERO_NEZ - } fu_op; - - typedef struct packed { - fu_t fu; - fu_op operation; - riscv::xlen_t operand_a; - riscv::xlen_t operand_b; - riscv::xlen_t imm; - logic [TRANS_ID_BITS-1:0] trans_id; - } fu_data_t; - - function automatic logic op_is_branch(input fu_op op); - unique case (op) inside - EQ, NE, LTS, GES, LTU, GEU: return 1'b1; - default: return 1'b0; // all other ops - endcase - endfunction - - // ------------------------------- - // Extract Src/Dst FP Reg from Op - // ------------------------------- - // function used in instr_trace svh - // is_rs1_fpr function is kept to allow cva6 compilation with instr_trace feature - function automatic logic is_rs1_fpr(input fu_op op); - unique case (op) inside - [FMUL : FNMADD], // Computational Operations (except ADD/SUB) - FCVT_F2I, // Float-Int Casts - FCVT_F2F, // Float-Float Casts - FSGNJ, // Sign Injections - FMV_F2X, // FPR-GPR Moves - FCMP, // Comparisons - FCLASS, // Classifications - [VFMIN : VFCPKCD_D], // Additional Vectorial FP ops - ACCEL_OP_FS1: - return 1'b1; // Accelerator instructions - default: return 1'b0; // all other ops - endcase - endfunction - - // function used in instr_trace svh - // is_rs2_fpr function is kept to allow cva6 compilation with instr_trace feature - function automatic logic is_rs2_fpr(input fu_op op); - unique case (op) inside - [FSD : FSB], // FP Stores - [FADD : FMIN_MAX], // Computational Operations (no sqrt) - [FMADD : FNMADD], // Fused Computational Operations - FCVT_F2F, // Vectorial F2F Conversions requrie target - [FSGNJ : FMV_F2X], // Sign Injections and moves mapped to SGNJ - FCMP, // Comparisons - [VFMIN : VFCPKCD_D]: - return 1'b1; // Additional Vectorial FP ops - default: return 1'b0; // all other ops - endcase - endfunction - - // function used in instr_trace svh - // is_imm_fpr function is kept to allow cva6 compilation with instr_trace feature - // ternary operations encode the rs3 address in the imm field, also add/sub - function automatic logic is_imm_fpr(input fu_op op); - unique case (op) inside - [FADD : FSUB], // ADD/SUB need inputs as Operand B/C - [FMADD : FNMADD], // Fused Computational Operations - [VFCPKAB_S : VFCPKCD_D]: - return 1'b1; // Vectorial FP cast and pack ops - default: return 1'b0; // all other ops - endcase - endfunction - - // function used in instr_trace svh - // is_rd_fpr function is kept to allow cva6 compilation with instr_trace feature - function automatic logic is_rd_fpr(input fu_op op); - unique case (op) inside - [FLD : FLB], // FP Loads - [FADD : FNMADD], // Computational Operations - FCVT_I2F, // Int-Float Casts - FCVT_F2F, // Float-Float Casts - FSGNJ, // Sign Injections - FMV_X2F, // GPR-FPR Moves - [VFMIN : VFSGNJX], // Vectorial MIN/MAX and SGNJ - [VFCPKAB_S : VFCPKCD_D], // Vectorial FP cast and pack ops - ACCEL_OP_FD: - return 1'b1; // Accelerator instructions - default: return 1'b0; // all other ops - endcase - endfunction - - function automatic logic is_amo(fu_op op); - case (op) inside - [AMO_LRW : AMO_MINDU]: begin - return 1'b1; - end - default: return 1'b0; - endcase - endfunction - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] vaddr; - logic overflow; - riscv::xlen_t data; - logic [(riscv::XLEN/8)-1:0] be; - fu_t fu; - fu_op operation; - logic [TRANS_ID_BITS-1:0] trans_id; - } lsu_ctrl_t; - - // --------------- - // IF/ID Stage - // --------------- - // store the decompressed instruction - typedef struct packed { - logic [riscv::VLEN-1:0] address; // the address of the instructions from below - logic [31:0] instruction; // instruction word - branchpredict_sbe_t branch_predict; // this field contains branch prediction information regarding the forward branch path - exception_t ex; // this field contains exceptions which might have happened earlier, e.g.: fetch exceptions - } fetch_entry_t; - - // --------------- - // ID/EX/WB Stage - // --------------- - - localparam RVFI = cva6_config_pkg::CVA6ConfigRvfiTrace; - - typedef struct packed { - logic [riscv::VLEN-1:0] pc; // PC of instruction - logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry - // with the transaction id in any case make the width more generic - fu_t fu; // functional unit to use - fu_op op; // operation to perform in each functional unit - logic [REG_ADDR_SIZE-1:0] rs1; // register source address 1 - logic [REG_ADDR_SIZE-1:0] rs2; // register source address 2 - logic [REG_ADDR_SIZE-1:0] rd; // register destination address - riscv::xlen_t result; // for unfinished instructions this field also holds the immediate, - // for unfinished floating-point that are partly encoded in rs2, this field also holds rs2 - // for unfinished floating-point fused operations (FMADD, FMSUB, FNMADD, FNMSUB) - // this field holds the address of the third operand from the floating-point register file - logic valid; // is the result valid - logic use_imm; // should we use the immediate as operand b? - logic use_zimm; // use zimm as operand a - logic use_pc; // set if we need to use the PC as operand a, PC from exception - exception_t ex; // exception has occurred - branchpredict_sbe_t bp; // branch predict scoreboard data structure - logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if - // we want jump accordingly e.g.: +4, +2 - riscv::xlen_t rs1_rdata; // information needed by RVFI - riscv::xlen_t rs2_rdata; // information needed by RVFI - logic [riscv::VLEN-1:0] lsu_addr; // information needed by RVFI - logic [(riscv::XLEN/8)-1:0] lsu_rmask; // information needed by RVFI - logic [(riscv::XLEN/8)-1:0] lsu_wmask; // information needed by RVFI - riscv::xlen_t lsu_wdata; // information needed by RVFI - logic vfp; // is this a vector floating-point instruction? - } scoreboard_entry_t; - - // --------------- - // MMU instanciation - // --------------- - localparam bit MMU_PRESENT = cva6_config_pkg::CVA6ConfigMmuPresent; - - localparam int unsigned INSTR_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigInstrTlbEntries; - localparam int unsigned DATA_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigDataTlbEntries; - - // ------------------- - // Performance counter - // ------------------- - localparam bit PERF_COUNTER_EN = cva6_config_pkg::CVA6ConfigPerfCounterEn; - localparam int unsigned MHPMCounterNum = 6; - - // -------------------- - // Atomics - // -------------------- - typedef enum logic [3:0] { - AMO_NONE = 4'b0000, - AMO_LR = 4'b0001, - AMO_SC = 4'b0010, - AMO_SWAP = 4'b0011, - AMO_ADD = 4'b0100, - AMO_AND = 4'b0101, - AMO_OR = 4'b0110, - AMO_XOR = 4'b0111, - AMO_MAX = 4'b1000, - AMO_MAXU = 4'b1001, - AMO_MIN = 4'b1010, - AMO_MINU = 4'b1011, - AMO_CAS1 = 4'b1100, // unused, not part of riscv spec, but provided in OpenPiton - AMO_CAS2 = 4'b1101 // unused, not part of riscv spec, but provided in OpenPiton - } amo_t; - - typedef struct packed { - logic valid; // valid flag - logic is_2M; // - logic is_1G; // - logic [27-1:0] vpn; // VPN (39bits) = 27bits + 12bits offset - logic [ASID_WIDTH-1:0] asid; - riscv::pte_t content; - } tlb_update_t; - - // Bits required for representation of physical address space as 4K pages - // (e.g. 27*4K == 39bit address space). - localparam PPN4K_WIDTH = 38; - - typedef struct packed { - logic valid; // valid flag - logic is_4M; // - logic [20-1:0] vpn; //VPN (32bits) = 20bits + 12bits offset - logic [9-1:0] asid; //ASID length = 9 for Sv32 mmu - riscv::pte_sv32_t content; - } tlb_update_sv32_t; - - typedef enum logic [1:0] { - FE_NONE, - FE_INSTR_ACCESS_FAULT, - FE_INSTR_PAGE_FAULT - } frontend_exception_t; - - // ---------------------- - // cache request ports - // ---------------------- - // I$ address translation requests - typedef struct packed { - logic fetch_valid; // address translation valid - logic [riscv::PLEN-1:0] fetch_paddr; // physical address in - exception_t fetch_exception; // exception occurred during fetch - } icache_areq_t; - - typedef struct packed { - logic fetch_req; // address translation request - logic [riscv::VLEN-1:0] fetch_vaddr; // virtual address out - } icache_arsp_t; - - // I$ data requests - typedef struct packed { - logic req; // we request a new word - logic kill_s1; // kill the current request - logic kill_s2; // kill the last request - logic spec; // request is speculative - logic [riscv::VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup - } icache_dreq_t; - - typedef struct packed { - logic ready; // icache is ready - logic valid; // signals a valid read - logic [FETCH_WIDTH-1:0] data; // 2+ cycle out: tag - logic [FETCH_USER_WIDTH-1:0] user; // User bits - logic [riscv::VLEN-1:0] vaddr; // virtual address out - exception_t ex; // we've encountered an exception - } icache_drsp_t; - - // AMO request going to cache. this request is unconditionally valid as soon - // as request goes high. - // Furthermore, those signals are kept stable until the response indicates - // completion by asserting ack. - typedef struct packed { - logic req; // this request is valid - amo_t amo_op; // atomic memory operation to perform - logic [1:0] size; // 2'b10 --> word operation, 2'b11 --> double word operation - logic [63:0] operand_a; // address - logic [63:0] operand_b; // data as layouted in the register - } amo_req_t; - - // AMO response coming from cache. - typedef struct packed { - logic ack; // response is valid - logic [63:0] result; // sign-extended, result - } amo_resp_t; - - // D$ data requests - typedef struct packed { - logic [DCACHE_INDEX_WIDTH-1:0] address_index; - logic [DCACHE_TAG_WIDTH-1:0] address_tag; - riscv::xlen_t data_wdata; - logic [DCACHE_USER_WIDTH-1:0] data_wuser; - logic data_req; - logic data_we; - logic [(riscv::XLEN/8)-1:0] data_be; - logic [1:0] data_size; - logic [DCACHE_TID_WIDTH-1:0] data_id; - logic kill_req; - logic tag_valid; - } dcache_req_i_t; - - typedef struct packed { - logic data_gnt; - logic data_rvalid; - logic [DCACHE_TID_WIDTH-1:0] data_rid; - riscv::xlen_t data_rdata; - logic [DCACHE_USER_WIDTH-1:0] data_ruser; - } dcache_req_o_t; - - // ---------------------- - // Arithmetic Functions - // ---------------------- - function automatic riscv::xlen_t sext32(logic [31:0] operand); - return {{riscv::XLEN - 32{operand[31]}}, operand[31:0]}; - endfunction - - // ---------------------- - // Immediate functions - // ---------------------- - function automatic logic [riscv::VLEN-1:0] uj_imm(logic [31:0] instruction_i); - return { - {44 + riscv::VLEN - 64{instruction_i[31]}}, - instruction_i[19:12], - instruction_i[20], - instruction_i[30:21], - 1'b0 - }; - endfunction - - function automatic logic [riscv::VLEN-1:0] i_imm(logic [31:0] instruction_i); - return {{52 + riscv::VLEN - 64{instruction_i[31]}}, instruction_i[31:20]}; - endfunction - - function automatic logic [riscv::VLEN-1:0] sb_imm(logic [31:0] instruction_i); - return { - {51 + riscv::VLEN - 64{instruction_i[31]}}, - instruction_i[31], - instruction_i[7], - instruction_i[30:25], - instruction_i[11:8], - 1'b0 - }; - endfunction - - // ---------------------- - // LSU Functions - // ---------------------- - // align data to address e.g.: shift data to be naturally 64 - function automatic riscv::xlen_t data_align(logic [2:0] addr, logic [63:0] data); - // Set addr[2] to 1'b0 when 32bits - logic [ 2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]}; - logic [63:0] data_tmp = {64{1'b0}}; - case (addr_tmp) - 3'b000: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-1:0]}; - 3'b001: - data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]}; - 3'b010: - data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]}; - 3'b011: - data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]}; - 3'b100: data_tmp = {data[31:0], data[63:32]}; - 3'b101: data_tmp = {data[23:0], data[63:24]}; - 3'b110: data_tmp = {data[15:0], data[63:16]}; - 3'b111: data_tmp = {data[7:0], data[63:8]}; - endcase - return data_tmp[riscv::XLEN-1:0]; - endfunction - - // generate byte enable mask - function automatic logic [7:0] be_gen(logic [2:0] addr, logic [1:0] size); - case (size) - 2'b11: begin - return 8'b1111_1111; - end - 2'b10: begin - case (addr[2:0]) - 3'b000: return 8'b0000_1111; - 3'b001: return 8'b0001_1110; - 3'b010: return 8'b0011_1100; - 3'b011: return 8'b0111_1000; - 3'b100: return 8'b1111_0000; - default: ; // Do nothing - endcase - end - 2'b01: begin - case (addr[2:0]) - 3'b000: return 8'b0000_0011; - 3'b001: return 8'b0000_0110; - 3'b010: return 8'b0000_1100; - 3'b011: return 8'b0001_1000; - 3'b100: return 8'b0011_0000; - 3'b101: return 8'b0110_0000; - 3'b110: return 8'b1100_0000; - default: ; // Do nothing - endcase - end - 2'b00: begin - case (addr[2:0]) - 3'b000: return 8'b0000_0001; - 3'b001: return 8'b0000_0010; - 3'b010: return 8'b0000_0100; - 3'b011: return 8'b0000_1000; - 3'b100: return 8'b0001_0000; - 3'b101: return 8'b0010_0000; - 3'b110: return 8'b0100_0000; - 3'b111: return 8'b1000_0000; - endcase - end - endcase - return 8'b0; - endfunction - - function automatic logic [3:0] be_gen_32(logic [1:0] addr, logic [1:0] size); - case (size) - 2'b10: begin - return 4'b1111; - end - 2'b01: begin - case (addr[1:0]) - 2'b00: return 4'b0011; - 2'b01: return 4'b0110; - 2'b10: return 4'b1100; - default: ; // Do nothing - endcase - end - 2'b00: begin - case (addr[1:0]) - 2'b00: return 4'b0001; - 2'b01: return 4'b0010; - 2'b10: return 4'b0100; - 2'b11: return 4'b1000; - endcase - end - default: return 4'b0; - endcase - return 4'b0; - endfunction - - // ---------------------- - // Extract Bytes from Op - // ---------------------- - function automatic logic [1:0] extract_transfer_size(fu_op op); - case (op) - LD, SD, FLD, FSD, - AMO_LRD, AMO_SCD, - AMO_SWAPD, AMO_ADDD, - AMO_ANDD, AMO_ORD, - AMO_XORD, AMO_MAXD, - AMO_MAXDU, AMO_MIND, - AMO_MINDU: begin - return 2'b11; - end - LW, LWU, SW, FLW, FSW, - AMO_LRW, AMO_SCW, - AMO_SWAPW, AMO_ADDW, - AMO_ANDW, AMO_ORW, - AMO_XORW, AMO_MAXW, - AMO_MAXWU, AMO_MINW, - AMO_MINWU: begin - return 2'b10; - end - LH, LHU, SH, FLH, FSH: return 2'b01; - LB, LBU, SB, FLB, FSB: return 2'b00; - default: return 2'b11; - endcase - endfunction + localparam int unsigned DCACHE_TID_WIDTH = cva6_config_pkg::CVA6ConfigDcacheIdWidth; + + localparam int unsigned WT_DCACHE_WBUF_DEPTH = cva6_config_pkg::CVA6ConfigWtDcacheWbufDepth; + + // --------------- + // EX Stage + // --------------- + + typedef enum logic [7:0] { // basic ALU op + ADD, SUB, ADDW, SUBW, + // logic operations + XORL, ORL, ANDL, + // shifts + SRA, SRL, SLL, SRLW, SLLW, SRAW, + // comparisons + LTS, LTU, GES, GEU, EQ, NE, + // jumps + JALR, BRANCH, + // set lower than operations + SLTS, SLTU, + // CSR functions + MRET, SRET, DRET, ECALL, WFI, FENCE, FENCE_I, SFENCE_VMA, CSR_WRITE, CSR_READ, CSR_SET, CSR_CLEAR, + // LSU functions + LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU, + // Atomic Memory Operations + AMO_LRW, AMO_LRD, AMO_SCW, AMO_SCD, + AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW, AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW, AMO_MINWU, + AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD, AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND, AMO_MINDU, + // Multiplications + MUL, MULH, MULHU, MULHSU, MULW, + // Divisions + DIV, DIVU, DIVW, DIVUW, REM, REMU, REMW, REMUW, + // Floating-Point Load and Store Instructions + FLD, FLW, FLH, FLB, FSD, FSW, FSH, FSB, + // Floating-Point Computational Instructions + FADD, FSUB, FMUL, FDIV, FMIN_MAX, FSQRT, FMADD, FMSUB, FNMSUB, FNMADD, + // Floating-Point Conversion and Move Instructions + FCVT_F2I, FCVT_I2F, FCVT_F2F, FSGNJ, FMV_F2X, FMV_X2F, + // Floating-Point Compare Instructions + FCMP, + // Floating-Point Classify Instruction + FCLASS, + // Vectorial Floating-Point Instructions that don't directly map onto the scalar ones + VFMIN, VFMAX, VFSGNJ, VFSGNJN, VFSGNJX, VFEQ, VFNE, VFLT, VFGE, VFLE, VFGT, VFCPKAB_S, VFCPKCD_S, VFCPKAB_D, VFCPKCD_D, + // Offload Instructions to be directed into cv_x_if + OFFLOAD, + // Or-Combine and REV8 + ORCB, REV8, + // Bitwise Rotation + ROL, ROLW, ROR, RORI, RORIW, RORW, + // Sign and Zero Extend + SEXTB, SEXTH, ZEXTH, + // Count population + CPOP, CPOPW, + // Count Leading/Training Zeros + CLZ, CLZW, CTZ, CTZW, + // Carry less multiplication Op's + CLMUL, CLMULH, CLMULR, + // Single bit instructions Op's + BCLR, BCLRI, BEXT, BEXTI, BINV, BINVI, BSET, BSETI, + // Integer minimum/maximum + MAX, MAXU, MIN, MINU, + // Shift with Add Unsigned Word and Unsigned Word Op's (Bitmanip) + SH1ADDUW, SH2ADDUW, SH3ADDUW, ADDUW, SLLIUW, + // Shift with Add (Bitmanip) + SH1ADD, SH2ADD, SH3ADD, + // Bitmanip Logical with negate op (Bitmanip) + ANDN, ORN, XNOR, + // Accelerator operations + ACCEL_OP, ACCEL_OP_FS1, ACCEL_OP_FD, ACCEL_OP_LOAD, ACCEL_OP_STORE + } fu_op; + + typedef struct packed { + fu_t fu; + fu_op operation; + riscv::xlen_t operand_a; + riscv::xlen_t operand_b; + riscv::xlen_t imm; + logic [TRANS_ID_BITS-1:0] trans_id; + } fu_data_t; + + function automatic logic op_is_branch (input fu_op op); + unique case (op) inside + EQ, NE, LTS, GES, LTU, GEU: return 1'b1; + default : return 1'b0; // all other ops + endcase + endfunction + + // ------------------------------- + // Extract Src/Dst FP Reg from Op + // ------------------------------- + function automatic logic is_rs1_fpr (input fu_op op); + if (FP_PRESENT) begin // makes function static for non-fp case + unique case (op) inside + [FMUL:FNMADD], // Computational Operations (except ADD/SUB) + FCVT_F2I, // Float-Int Casts + FCVT_F2F, // Float-Float Casts + FSGNJ, // Sign Injections + FMV_F2X, // FPR-GPR Moves + FCMP, // Comparisons + FCLASS, // Classifications + [VFMIN:VFCPKCD_D], // Additional Vectorial FP ops + ACCEL_OP_FS1 : return 1'b1; // Accelerator instructions + default : return 1'b0; // all other ops + endcase + end else + return 1'b0; + endfunction + + function automatic logic is_rs2_fpr (input fu_op op); + if (FP_PRESENT) begin // makes function static for non-fp case + unique case (op) inside + [FSD:FSB], // FP Stores + [FADD:FMIN_MAX], // Computational Operations (no sqrt) + [FMADD:FNMADD], // Fused Computational Operations + FCVT_F2F, // Vectorial F2F Conversions requrie target + [FSGNJ:FMV_F2X], // Sign Injections and moves mapped to SGNJ + FCMP, // Comparisons + [VFMIN:VFCPKCD_D] : return 1'b1; // Additional Vectorial FP ops + default : return 1'b0; // all other ops + endcase + end else + return 1'b0; + endfunction + + // ternary operations encode the rs3 address in the imm field, also add/sub + function automatic logic is_imm_fpr (input fu_op op); + if (FP_PRESENT) begin // makes function static for non-fp case + unique case (op) inside + [FADD:FSUB], // ADD/SUB need inputs as Operand B/C + [FMADD:FNMADD], // Fused Computational Operations + [VFCPKAB_S:VFCPKCD_D] : return 1'b1; // Vectorial FP cast and pack ops + default : return 1'b0; // all other ops + endcase + end else + return 1'b0; + endfunction + + function automatic logic is_rd_fpr (input fu_op op); + if (FP_PRESENT) begin // makes function static for non-fp case + unique case (op) inside + [FLD:FLB], // FP Loads + [FADD:FNMADD], // Computational Operations + FCVT_I2F, // Int-Float Casts + FCVT_F2F, // Float-Float Casts + FSGNJ, // Sign Injections + FMV_X2F, // GPR-FPR Moves + [VFMIN:VFSGNJX], // Vectorial MIN/MAX and SGNJ + [VFCPKAB_S:VFCPKCD_D], // Vectorial FP cast and pack ops + ACCEL_OP_FD : return 1'b1; // Accelerator instructions + default : return 1'b0; // all other ops + endcase + end else + return 1'b0; + endfunction + + function automatic logic is_amo (fu_op op); + case (op) inside + [AMO_LRW:AMO_MINDU]: begin + return 1'b1; + end + default: return 1'b0; + endcase + endfunction + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] vaddr; + logic overflow; + riscv::xlen_t data; + logic [(riscv::XLEN/8)-1:0] be; + fu_t fu; + fu_op operation; + logic [TRANS_ID_BITS-1:0] trans_id; + } lsu_ctrl_t; + + // --------------- + // IF/ID Stage + // --------------- + // store the decompressed instruction + typedef struct packed { + logic [riscv::VLEN-1:0] address; // the address of the instructions from below + logic [31:0] instruction; // instruction word + branchpredict_sbe_t branch_predict; // this field contains branch prediction information regarding the forward branch path + exception_t ex; // this field contains exceptions which might have happened earlier, e.g.: fetch exceptions + } fetch_entry_t; + + // --------------- + // ID/EX/WB Stage + // --------------- + + localparam RVFI = cva6_config_pkg::CVA6ConfigRvfiTrace; + typedef rvfi_pkg::rvfi_instr_t [NR_COMMIT_PORTS-1:0] rvfi_port_t; + + typedef struct packed { + logic [riscv::VLEN-1:0] pc; // PC of instruction + logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry + // with the transaction id in any case make the width more generic + fu_t fu; // functional unit to use + fu_op op; // operation to perform in each functional unit + logic [REG_ADDR_SIZE-1:0] rs1; // register source address 1 + logic [REG_ADDR_SIZE-1:0] rs2; // register source address 2 + logic [REG_ADDR_SIZE-1:0] rd; // register destination address + riscv::xlen_t result; // for unfinished instructions this field also holds the immediate, + // for unfinished floating-point that are partly encoded in rs2, this field also holds rs2 + // for unfinished floating-point fused operations (FMADD, FMSUB, FNMADD, FNMSUB) + // this field holds the address of the third operand from the floating-point register file + logic valid; // is the result valid + logic use_imm; // should we use the immediate as operand b? + logic use_zimm; // use zimm as operand a + logic use_pc; // set if we need to use the PC as operand a, PC from exception + exception_t ex; // exception has occurred + branchpredict_sbe_t bp; // branch predict scoreboard data structure + logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if + // we want jump accordingly e.g.: +4, +2 + riscv::xlen_t rs1_rdata; // information needed by RVFI + riscv::xlen_t rs2_rdata; // information needed by RVFI + logic [riscv::VLEN-1:0] lsu_addr; // information needed by RVFI + logic [(riscv::XLEN/8)-1:0] lsu_rmask; // information needed by RVFI + logic [(riscv::XLEN/8)-1:0] lsu_wmask; // information needed by RVFI + riscv::xlen_t lsu_wdata; // information needed by RVFI + logic vfp; // is this a vector floating-point instruction? + } scoreboard_entry_t; + + // --------------- + // MMU instanciation + // --------------- + localparam bit MMU_PRESENT = cva6_config_pkg::CVA6ConfigMmuPresent; + + localparam int unsigned INSTR_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigInstrTlbEntries; + localparam int unsigned DATA_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigDataTlbEntries; + + // ------------------- + // Performance counter + // ------------------- + localparam bit PERF_COUNTER_EN = cva6_config_pkg::CVA6ConfigPerfCounterEn; + localparam int unsigned MHPMCounterNum = 6; + + // -------------------- + // Atomics + // -------------------- + typedef enum logic [3:0] { + AMO_NONE =4'b0000, + AMO_LR =4'b0001, + AMO_SC =4'b0010, + AMO_SWAP =4'b0011, + AMO_ADD =4'b0100, + AMO_AND =4'b0101, + AMO_OR =4'b0110, + AMO_XOR =4'b0111, + AMO_MAX =4'b1000, + AMO_MAXU =4'b1001, + AMO_MIN =4'b1010, + AMO_MINU =4'b1011, + AMO_CAS1 =4'b1100, // unused, not part of riscv spec, but provided in OpenPiton + AMO_CAS2 =4'b1101 // unused, not part of riscv spec, but provided in OpenPiton + } amo_t; + + typedef struct packed { + logic valid; // valid flag + logic is_2M; // + logic is_1G; // + logic [27-1:0] vpn; // VPN (39bits) = 27bits + 12bits offset + logic [ASID_WIDTH-1:0] asid; + riscv::pte_t content; + } tlb_update_t; + + // Bits required for representation of physical address space as 4K pages + // (e.g. 27*4K == 39bit address space). + localparam PPN4K_WIDTH = 38; + + typedef struct packed { + logic valid; // valid flag + logic is_4M; // + logic [20-1:0] vpn; //VPN (32bits) = 20bits + 12bits offset + logic [9-1:0] asid; //ASID length = 9 for Sv32 mmu + riscv::pte_sv32_t content; + } tlb_update_sv32_t; + + typedef struct packed { + logic valid; // valid flag + logic is_2M; // + logic is_4M; // + logic is_1G; // + logic [VPN_LEN-1:0] vpn; // + logic [ASID_LEN-1:0] asid; // + riscv::pte_cva6_t content; + } tlb_update_cva6_t; + + typedef enum logic [1:0] { + FE_NONE, + FE_INSTR_ACCESS_FAULT, + FE_INSTR_PAGE_FAULT + } frontend_exception_t; + + // ---------------------- + // cache request ports + // ---------------------- + // I$ address translation requests + typedef struct packed { + logic fetch_valid; // address translation valid + logic [riscv::PLEN-1:0] fetch_paddr; // physical address in + exception_t fetch_exception; // exception occurred during fetch + } icache_areq_i_t; + + typedef struct packed { + logic fetch_req; // address translation request + logic [riscv::VLEN-1:0] fetch_vaddr; // virtual address out + } icache_areq_o_t; + + // I$ data requests + typedef struct packed { + logic req; // we request a new word + logic kill_s1; // kill the current request + logic kill_s2; // kill the last request + logic spec; // request is speculative + logic [riscv::VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup + } icache_dreq_i_t; + + typedef struct packed { + logic ready; // icache is ready + logic valid; // signals a valid read + logic [FETCH_WIDTH-1:0] data; // 2+ cycle out: tag + logic [FETCH_USER_WIDTH-1:0] user; // User bits + logic [riscv::VLEN-1:0] vaddr; // virtual address out + exception_t ex; // we've encountered an exception + } icache_dreq_o_t; + + // AMO request going to cache. this request is unconditionally valid as soon + // as request goes high. + // Furthermore, those signals are kept stable until the response indicates + // completion by asserting ack. + typedef struct packed { + logic req; // this request is valid + amo_t amo_op; // atomic memory operation to perform + logic [1:0] size; // 2'b10 --> word operation, 2'b11 --> double word operation + logic [63:0] operand_a; // address + logic [63:0] operand_b; // data as layouted in the register + } amo_req_t; + + // AMO response coming from cache. + typedef struct packed { + logic ack; // response is valid + logic [63:0] result; // sign-extended, result + } amo_resp_t; + + // D$ data requests + typedef struct packed { + logic [DCACHE_INDEX_WIDTH-1:0] address_index; + logic [DCACHE_TAG_WIDTH-1:0] address_tag; + riscv::xlen_t data_wdata; + logic [DCACHE_USER_WIDTH-1:0] data_wuser; + logic data_req; + logic data_we; + logic [(riscv::XLEN/8)-1:0] data_be; + logic [1:0] data_size; + logic [DCACHE_TID_WIDTH-1:0] data_id; + logic kill_req; + logic tag_valid; + } dcache_req_i_t; + + typedef struct packed { + logic data_gnt; + logic data_rvalid; + logic [DCACHE_TID_WIDTH-1:0] data_rid; + riscv::xlen_t data_rdata; + logic [DCACHE_USER_WIDTH-1:0] data_ruser; + } dcache_req_o_t; + + // ---------------------- + // Arithmetic Functions + // ---------------------- + function automatic riscv::xlen_t sext32 (logic [31:0] operand); + return {{riscv::XLEN-32{operand[31]}}, operand[31:0]}; + endfunction + + // ---------------------- + // Immediate functions + // ---------------------- + function automatic logic [riscv::VLEN-1:0] uj_imm (logic [31:0] instruction_i); + return { {44+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[19:12], instruction_i[20], instruction_i[30:21], 1'b0 }; + endfunction + + function automatic logic [riscv::VLEN-1:0] i_imm (logic [31:0] instruction_i); + return { {52+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31:20] }; + endfunction + + function automatic logic [riscv::VLEN-1:0] sb_imm (logic [31:0] instruction_i); + return { {51+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31], instruction_i[7], instruction_i[30:25], instruction_i[11:8], 1'b0 }; + endfunction + + // ---------------------- + // LSU Functions + // ---------------------- + // align data to address e.g.: shift data to be naturally 64 + function automatic riscv::xlen_t data_align (logic [2:0] addr, logic [63:0] data); + // Set addr[2] to 1'b0 when 32bits + logic [2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]}; + logic [63:0] data_tmp = {64{1'b0}}; + case (addr_tmp) + 3'b000: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-1:0]}; + 3'b001: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]}; + 3'b010: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]}; + 3'b011: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]}; + 3'b100: data_tmp = {data[31:0], data[63:32]}; + 3'b101: data_tmp = {data[23:0], data[63:24]}; + 3'b110: data_tmp = {data[15:0], data[63:16]}; + 3'b111: data_tmp = {data[7:0], data[63:8]}; + endcase + return data_tmp[riscv::XLEN-1:0]; + endfunction + + // generate byte enable mask + function automatic logic [7:0] be_gen(logic [2:0] addr, logic [1:0] size); + case (size) + 2'b11: begin + return 8'b1111_1111; + end + 2'b10: begin + case (addr[2:0]) + 3'b000: return 8'b0000_1111; + 3'b001: return 8'b0001_1110; + 3'b010: return 8'b0011_1100; + 3'b011: return 8'b0111_1000; + 3'b100: return 8'b1111_0000; + default: ; // Do nothing + endcase + end + 2'b01: begin + case (addr[2:0]) + 3'b000: return 8'b0000_0011; + 3'b001: return 8'b0000_0110; + 3'b010: return 8'b0000_1100; + 3'b011: return 8'b0001_1000; + 3'b100: return 8'b0011_0000; + 3'b101: return 8'b0110_0000; + 3'b110: return 8'b1100_0000; + default: ; // Do nothing + endcase + end + 2'b00: begin + case (addr[2:0]) + 3'b000: return 8'b0000_0001; + 3'b001: return 8'b0000_0010; + 3'b010: return 8'b0000_0100; + 3'b011: return 8'b0000_1000; + 3'b100: return 8'b0001_0000; + 3'b101: return 8'b0010_0000; + 3'b110: return 8'b0100_0000; + 3'b111: return 8'b1000_0000; + endcase + end + endcase + return 8'b0; + endfunction + + function automatic logic [3:0] be_gen_32(logic [1:0] addr, logic [1:0] size); + case (size) + 2'b10: begin + return 4'b1111; + end + 2'b01: begin + case (addr[1:0]) + 2'b00: return 4'b0011; + 2'b01: return 4'b0110; + 2'b10: return 4'b1100; + default: ; // Do nothing + endcase + end + 2'b00: begin + case (addr[1:0]) + 2'b00: return 4'b0001; + 2'b01: return 4'b0010; + 2'b10: return 4'b0100; + 2'b11: return 4'b1000; + endcase + end + default: return 4'b0; + endcase + return 4'b0; + endfunction + + // ---------------------- + // Extract Bytes from Op + // ---------------------- + function automatic logic [1:0] extract_transfer_size(fu_op op); + case (op) + LD, SD, FLD, FSD, + AMO_LRD, AMO_SCD, + AMO_SWAPD, AMO_ADDD, + AMO_ANDD, AMO_ORD, + AMO_XORD, AMO_MAXD, + AMO_MAXDU, AMO_MIND, + AMO_MINDU: begin + return 2'b11; + end + LW, LWU, SW, FLW, FSW, + AMO_LRW, AMO_SCW, + AMO_SWAPW, AMO_ADDW, + AMO_ANDW, AMO_ORW, + AMO_XORW, AMO_MAXW, + AMO_MAXWU, AMO_MINW, + AMO_MINWU: begin + return 2'b10; + end + LH, LHU, SH, FLH, FSH: return 2'b01; + LB, LBU, SB, FLB, FSB: return 2'b00; + default: return 2'b11; + endcase + endfunction endpackage diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 2a9d919c1a..67aa084569 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -15,835 +15,841 @@ * Description: Common RISC-V definitions. */ -package riscv; - - // ---------------------- - // Import cva6 config from cva6_config_pkg - // ---------------------- - localparam XLEN = cva6_config_pkg::CVA6ConfigXlen; - localparam FPU_EN = cva6_config_pkg::CVA6ConfigFpuEn; - - // ---------------------- - // Data and Address length - // ---------------------- - typedef enum logic [3:0] { + package riscv; + + // ---------------------- + // Import cva6 config from cva6_config_pkg + // ---------------------- + localparam XLEN = cva6_config_pkg::CVA6ConfigXlen; + localparam FPU_EN = cva6_config_pkg::CVA6ConfigFpuEn; + + // ---------------------- + // Data and Address length + // ---------------------- + typedef enum logic [3:0] { ModeOff = 0, ModeSv32 = 1, ModeSv39 = 8, ModeSv48 = 9, ModeSv57 = 10, ModeSv64 = 11 - } vm_mode_t; - - // Warning: When using STD_CACHE, configuration must be PLEN=56 and VLEN=64 - // Warning: VLEN must be superior or equal to PLEN - localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length - localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length - - localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; - localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; - localparam ModeW = (XLEN == 32) ? 1 : 4; - localparam ASIDW = (XLEN == 32) ? 9 : 16; - localparam PPNW = (XLEN == 32) ? 22 : 44; - localparam vm_mode_t MODE_SV = (XLEN == 32) ? ModeSv32 : ModeSv39; - localparam SV = (MODE_SV == ModeSv32) ? 32 : 39; - localparam VPN2 = (VLEN - 31 < 8) ? VLEN - 31 : 8; - localparam XLEN_ALIGN_BYTES = $clog2(XLEN / 8); - - typedef logic [XLEN-1:0] xlen_t; - - // -------------------- - // Privilege Spec - // -------------------- - typedef enum logic [1:0] { - PRIV_LVL_M = 2'b11, - PRIV_LVL_S = 2'b01, - PRIV_LVL_U = 2'b00 - } priv_lvl_t; - - // type which holds xlen - typedef enum logic [1:0] { - XLEN_32 = 2'b01, - XLEN_64 = 2'b10, - XLEN_128 = 2'b11 - } xlen_e; - - typedef enum logic [1:0] { - Off = 2'b00, - Initial = 2'b01, - Clean = 2'b10, - Dirty = 2'b11 - } xs_t; - - typedef struct packed { - logic sd; // signal dirty state - read-only - logic [62:34] wpri6; // writes preserved reads ignored - xlen_e uxl; // variable user mode xlen - hardwired to zero - logic [12:0] wpri5; // writes preserved reads ignored - logic mxr; // make executable readable - logic sum; // permit supervisor user memory access - logic wpri4; // writes preserved reads ignored - xs_t xs; // extension register - hardwired to zero - xs_t fs; // floating point extension register - logic [1:0] wpri3; // writes preserved reads ignored - xs_t vs; // vector extension register - logic spp; // holds the previous privilege mode up to supervisor - logic wpri2; // writes preserved reads ignored - logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) - logic spie; // supervisor interrupts enable bit active prior to trap - logic [1:0] wpri1; // writes preserved reads ignored - logic sie; // supervisor interrupts enable - logic wpri0; // writes preserved reads ignored - } sstatus_rv_t; - - typedef struct packed { - logic sd; // signal dirty state - read-only - logic [62:36] wpri4; // writes preserved reads ignored - xlen_e sxl; // variable supervisor mode xlen - hardwired to zero - xlen_e uxl; // variable user mode xlen - hardwired to zero - logic [8:0] wpri3; // writes preserved reads ignored - logic tsr; // trap sret - logic tw; // time wait - logic tvm; // trap virtual memory - logic mxr; // make executable readable - logic sum; // permit supervisor user memory access - logic mprv; // modify privilege - privilege level for ld/st - xs_t xs; // extension register - hardwired to zero - xs_t fs; // floating point extension register - priv_lvl_t mpp; // holds the previous privilege mode up to machine - xs_t vs; // vector extension register - logic spp; // holds the previous privilege mode up to supervisor - logic mpie; // machine interrupts enable bit active prior to trap - logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) - logic spie; // supervisor interrupts enable bit active prior to trap - logic wpri2; // writes preserved reads ignored - logic mie; // machine interrupts enable - logic wpri1; // writes preserved reads ignored - logic sie; // supervisor interrupts enable - logic wpri0; // writes preserved reads ignored - } mstatus_rv_t; - - typedef struct packed { - logic [ModeW-1:0] mode; - logic [ASIDW-1:0] asid; - logic [PPNW-1:0] ppn; - } satp_t; - - // -------------------- - // Instruction Types - // -------------------- - typedef struct packed { - logic [31:25] funct7; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } rtype_t; - - typedef struct packed { - logic [31:27] rs3; - logic [26:25] funct2; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } r4type_t; - - typedef struct packed { - logic [31:27] funct5; - logic [26:25] fmt; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] rm; - logic [11:7] rd; - logic [6:0] opcode; - } rftype_t; // floating-point - - typedef struct packed { - logic [31:30] funct2; - logic [29:25] vecfltop; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:14] repl; - logic [13:12] vfmt; - logic [11:7] rd; - logic [6:0] opcode; - } rvftype_t; // vectorial floating-point - - typedef struct packed { - logic [31:20] imm; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } itype_t; - - typedef struct packed { - logic [31:25] imm; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] imm0; - logic [6:0] opcode; - } stype_t; - - typedef struct packed { - logic [31:12] imm; - logic [11:7] rd; - logic [6:0] opcode; - } utype_t; - - // atomic instructions - typedef struct packed { - logic [31:27] funct5; - logic aq; - logic rl; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } atype_t; - - typedef union packed { - logic [31:0] instr; - rtype_t rtype; - r4type_t r4type; - rftype_t rftype; - rvftype_t rvftype; - itype_t itype; - stype_t stype; - utype_t utype; - atype_t atype; - } instruction_t; - - // -------------------- - // Opcodes - // -------------------- - // RV32/64G listings: - // Quadrant 0 - localparam OpcodeLoad = 7'b00_000_11; - localparam OpcodeLoadFp = 7'b00_001_11; - localparam OpcodeCustom0 = 7'b00_010_11; - localparam OpcodeMiscMem = 7'b00_011_11; - localparam OpcodeOpImm = 7'b00_100_11; - localparam OpcodeAuipc = 7'b00_101_11; - localparam OpcodeOpImm32 = 7'b00_110_11; - // Quadrant 1 - localparam OpcodeStore = 7'b01_000_11; - localparam OpcodeStoreFp = 7'b01_001_11; - localparam OpcodeCustom1 = 7'b01_010_11; - localparam OpcodeAmo = 7'b01_011_11; - localparam OpcodeOp = 7'b01_100_11; - localparam OpcodeLui = 7'b01_101_11; - localparam OpcodeOp32 = 7'b01_110_11; - // Quadrant 2 - localparam OpcodeMadd = 7'b10_000_11; - localparam OpcodeMsub = 7'b10_001_11; - localparam OpcodeNmsub = 7'b10_010_11; - localparam OpcodeNmadd = 7'b10_011_11; - localparam OpcodeOpFp = 7'b10_100_11; - localparam OpcodeVec = 7'b10_101_11; - localparam OpcodeCustom2 = 7'b10_110_11; - // Quadrant 3 - localparam OpcodeBranch = 7'b11_000_11; - localparam OpcodeJalr = 7'b11_001_11; - localparam OpcodeRsrvd2 = 7'b11_010_11; - localparam OpcodeJal = 7'b11_011_11; - localparam OpcodeSystem = 7'b11_100_11; - localparam OpcodeRsrvd3 = 7'b11_101_11; - localparam OpcodeCustom3 = 7'b11_110_11; - - // RV64C/RV32C listings: - // Quadrant 0 - localparam OpcodeC0 = 2'b00; - localparam OpcodeC0Addi4spn = 3'b000; - localparam OpcodeC0Fld = 3'b001; - localparam OpcodeC0Lw = 3'b010; - localparam OpcodeC0Ld = 3'b011; - localparam OpcodeC0Zcb = 3'b100; - localparam OpcodeC0Fsd = 3'b101; - localparam OpcodeC0Sw = 3'b110; - localparam OpcodeC0Sd = 3'b111; - // Quadrant 1 - localparam OpcodeC1 = 2'b01; - localparam OpcodeC1Addi = 3'b000; - localparam OpcodeC1Addiw = 3'b001; //for RV64I only - localparam OpcodeC1Jal = 3'b001; //for RV32I only - localparam OpcodeC1Li = 3'b010; - localparam OpcodeC1LuiAddi16sp = 3'b011; - localparam OpcodeC1MiscAlu = 3'b100; - localparam OpcodeC1J = 3'b101; - localparam OpcodeC1Beqz = 3'b110; - localparam OpcodeC1Bnez = 3'b111; - // Quadrant 2 - localparam OpcodeC2 = 2'b10; - localparam OpcodeC2Slli = 3'b000; - localparam OpcodeC2Fldsp = 3'b001; - localparam OpcodeC2Lwsp = 3'b010; - localparam OpcodeC2Ldsp = 3'b011; - localparam OpcodeC2JalrMvAdd = 3'b100; - localparam OpcodeC2Fsdsp = 3'b101; - localparam OpcodeC2Swsp = 3'b110; - localparam OpcodeC2Sdsp = 3'b111; - - // ---------------------- - // Virtual Memory - // ---------------------- - // memory management, pte for sv39 - typedef struct packed { - logic [9:0] reserved; - logic [44-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; - } pte_t; - - // memory management, pte for sv32 - typedef struct packed { - logic [22-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; - } pte_sv32_t; - - // ---------------------- - // Exception Cause Codes - // ---------------------- - localparam logic [XLEN-1:0] INSTR_ADDR_MISALIGNED = 0; - localparam logic [XLEN-1:0] INSTR_ACCESS_FAULT = 1; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ILLEGAL_INSTR = 2; - localparam logic [XLEN-1:0] BREAKPOINT = 3; - localparam logic [XLEN-1:0] LD_ADDR_MISALIGNED = 4; - localparam logic [XLEN-1:0] LD_ACCESS_FAULT = 5; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ST_ADDR_MISALIGNED = 6; - localparam logic [XLEN-1:0] ST_ACCESS_FAULT = 7; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode - localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from supervisor mode - localparam logic [XLEN-1:0] ENV_CALL_MMODE = 11; // environment call from machine mode - localparam logic [XLEN-1:0] INSTR_PAGE_FAULT = 12; // Instruction page fault - localparam logic [XLEN-1:0] LOAD_PAGE_FAULT = 13; // Load page fault - localparam logic [XLEN-1:0] STORE_PAGE_FAULT = 15; // Store page fault - localparam logic [XLEN-1:0] DEBUG_REQUEST = 24; // Debug request - - localparam int unsigned IRQ_S_SOFT = 1; - localparam int unsigned IRQ_M_SOFT = 3; - localparam int unsigned IRQ_S_TIMER = 5; - localparam int unsigned IRQ_M_TIMER = 7; - localparam int unsigned IRQ_S_EXT = 9; - localparam int unsigned IRQ_M_EXT = 11; - - localparam logic [XLEN-1:0] MIP_SSIP = 1 << IRQ_S_SOFT; - localparam logic [XLEN-1:0] MIP_MSIP = 1 << IRQ_M_SOFT; - localparam logic [XLEN-1:0] MIP_STIP = 1 << IRQ_S_TIMER; - localparam logic [XLEN-1:0] MIP_MTIP = 1 << IRQ_M_TIMER; - localparam logic [XLEN-1:0] MIP_SEIP = 1 << IRQ_S_EXT; - localparam logic [XLEN-1:0] MIP_MEIP = 1 << IRQ_M_EXT; - - localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_SOFT); - localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_SOFT); - localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_TIMER); - localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_TIMER); - localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_EXT); - localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_EXT); - - // ----- - // CSRs - // ----- - typedef enum logic [11:0] { - // Floating-Point CSRs - CSR_FFLAGS = 12'h001, - CSR_FRM = 12'h002, - CSR_FCSR = 12'h003, - CSR_FTRAN = 12'h800, - // Vector CSRs - CSR_VSTART = 12'h008, - CSR_VXSAT = 12'h009, - CSR_VXRM = 12'h00A, - CSR_VCSR = 12'h00F, - CSR_VL = 12'hC20, - CSR_VTYPE = 12'hC21, - CSR_VLENB = 12'hC22, - // Supervisor Mode CSRs - CSR_SSTATUS = 12'h100, - CSR_SIE = 12'h104, - CSR_STVEC = 12'h105, - CSR_SCOUNTEREN = 12'h106, - CSR_SSCRATCH = 12'h140, - CSR_SEPC = 12'h141, - CSR_SCAUSE = 12'h142, - CSR_STVAL = 12'h143, - CSR_SIP = 12'h144, - CSR_SATP = 12'h180, - // Machine Mode CSRs - CSR_MSTATUS = 12'h300, - CSR_MISA = 12'h301, - CSR_MEDELEG = 12'h302, - CSR_MIDELEG = 12'h303, - CSR_MIE = 12'h304, - CSR_MTVEC = 12'h305, - CSR_MCOUNTEREN = 12'h306, - CSR_MSTATUSH = 12'h310, - CSR_MCOUNTINHIBIT = 12'h320, - CSR_MHPM_EVENT_3 = 12'h323, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_4 = 12'h324, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_5 = 12'h325, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_6 = 12'h326, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_7 = 12'h327, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_8 = 12'h328, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_9 = 12'h329, //Reserved - CSR_MHPM_EVENT_10 = 12'h32A, //Reserved - CSR_MHPM_EVENT_11 = 12'h32B, //Reserved - CSR_MHPM_EVENT_12 = 12'h32C, //Reserved - CSR_MHPM_EVENT_13 = 12'h32D, //Reserved - CSR_MHPM_EVENT_14 = 12'h32E, //Reserved - CSR_MHPM_EVENT_15 = 12'h32F, //Reserved - CSR_MHPM_EVENT_16 = 12'h330, //Reserved - CSR_MHPM_EVENT_17 = 12'h331, //Reserved - CSR_MHPM_EVENT_18 = 12'h332, //Reserved - CSR_MHPM_EVENT_19 = 12'h333, //Reserved - CSR_MHPM_EVENT_20 = 12'h334, //Reserved - CSR_MHPM_EVENT_21 = 12'h335, //Reserved - CSR_MHPM_EVENT_22 = 12'h336, //Reserved - CSR_MHPM_EVENT_23 = 12'h337, //Reserved - CSR_MHPM_EVENT_24 = 12'h338, //Reserved - CSR_MHPM_EVENT_25 = 12'h339, //Reserved - CSR_MHPM_EVENT_26 = 12'h33A, //Reserved - CSR_MHPM_EVENT_27 = 12'h33B, //Reserved - CSR_MHPM_EVENT_28 = 12'h33C, //Reserved - CSR_MHPM_EVENT_29 = 12'h33D, //Reserved - CSR_MHPM_EVENT_30 = 12'h33E, //Reserved - CSR_MHPM_EVENT_31 = 12'h33F, //Reserved - CSR_MSCRATCH = 12'h340, - CSR_MEPC = 12'h341, - CSR_MCAUSE = 12'h342, - CSR_MTVAL = 12'h343, - CSR_MIP = 12'h344, - CSR_PMPCFG0 = 12'h3A0, - CSR_PMPCFG1 = 12'h3A1, - CSR_PMPCFG2 = 12'h3A2, - CSR_PMPCFG3 = 12'h3A3, - CSR_PMPADDR0 = 12'h3B0, - CSR_PMPADDR1 = 12'h3B1, - CSR_PMPADDR2 = 12'h3B2, - CSR_PMPADDR3 = 12'h3B3, - CSR_PMPADDR4 = 12'h3B4, - CSR_PMPADDR5 = 12'h3B5, - CSR_PMPADDR6 = 12'h3B6, - CSR_PMPADDR7 = 12'h3B7, - CSR_PMPADDR8 = 12'h3B8, - CSR_PMPADDR9 = 12'h3B9, - CSR_PMPADDR10 = 12'h3BA, - CSR_PMPADDR11 = 12'h3BB, - CSR_PMPADDR12 = 12'h3BC, - CSR_PMPADDR13 = 12'h3BD, - CSR_PMPADDR14 = 12'h3BE, - CSR_PMPADDR15 = 12'h3BF, - CSR_MVENDORID = 12'hF11, - CSR_MARCHID = 12'hF12, - CSR_MIMPID = 12'hF13, - CSR_MHARTID = 12'hF14, - CSR_MCONFIGPTR = 12'hF15, - CSR_MCYCLE = 12'hB00, - CSR_MCYCLEH = 12'hB80, - CSR_MINSTRET = 12'hB02, - CSR_MINSTRETH = 12'hB82, - //Performance Counters - CSR_MHPM_COUNTER_3 = 12'hB03, - CSR_MHPM_COUNTER_4 = 12'hB04, - CSR_MHPM_COUNTER_5 = 12'hB05, - CSR_MHPM_COUNTER_6 = 12'hB06, - CSR_MHPM_COUNTER_7 = 12'hB07, - CSR_MHPM_COUNTER_8 = 12'hB08, - CSR_MHPM_COUNTER_9 = 12'hB09, // reserved - CSR_MHPM_COUNTER_10 = 12'hB0A, // reserved - CSR_MHPM_COUNTER_11 = 12'hB0B, // reserved - CSR_MHPM_COUNTER_12 = 12'hB0C, // reserved - CSR_MHPM_COUNTER_13 = 12'hB0D, // reserved - CSR_MHPM_COUNTER_14 = 12'hB0E, // reserved - CSR_MHPM_COUNTER_15 = 12'hB0F, // reserved - CSR_MHPM_COUNTER_16 = 12'hB10, // reserved - CSR_MHPM_COUNTER_17 = 12'hB11, // reserved - CSR_MHPM_COUNTER_18 = 12'hB12, // reserved - CSR_MHPM_COUNTER_19 = 12'hB13, // reserved - CSR_MHPM_COUNTER_20 = 12'hB14, // reserved - CSR_MHPM_COUNTER_21 = 12'hB15, // reserved - CSR_MHPM_COUNTER_22 = 12'hB16, // reserved - CSR_MHPM_COUNTER_23 = 12'hB17, // reserved - CSR_MHPM_COUNTER_24 = 12'hB18, // reserved - CSR_MHPM_COUNTER_25 = 12'hB19, // reserved - CSR_MHPM_COUNTER_26 = 12'hB1A, // reserved - CSR_MHPM_COUNTER_27 = 12'hB1B, // reserved - CSR_MHPM_COUNTER_28 = 12'hB1C, // reserved - CSR_MHPM_COUNTER_29 = 12'hB1D, // reserved - CSR_MHPM_COUNTER_30 = 12'hB1E, // reserved - CSR_MHPM_COUNTER_31 = 12'hB1F, // reserved - CSR_MHPM_COUNTER_3H = 12'hB83, - CSR_MHPM_COUNTER_4H = 12'hB84, - CSR_MHPM_COUNTER_5H = 12'hB85, - CSR_MHPM_COUNTER_6H = 12'hB86, - CSR_MHPM_COUNTER_7H = 12'hB87, - CSR_MHPM_COUNTER_8H = 12'hB88, - CSR_MHPM_COUNTER_9H = 12'hB89, // reserved - CSR_MHPM_COUNTER_10H = 12'hB8A, // reserved - CSR_MHPM_COUNTER_11H = 12'hB8B, // reserved - CSR_MHPM_COUNTER_12H = 12'hB8C, // reserved - CSR_MHPM_COUNTER_13H = 12'hB8D, // reserved - CSR_MHPM_COUNTER_14H = 12'hB8E, // reserved - CSR_MHPM_COUNTER_15H = 12'hB8F, // reserved - CSR_MHPM_COUNTER_16H = 12'hB90, // reserved - CSR_MHPM_COUNTER_17H = 12'hB91, // reserved - CSR_MHPM_COUNTER_18H = 12'hB92, // reserved - CSR_MHPM_COUNTER_19H = 12'hB93, // reserved - CSR_MHPM_COUNTER_20H = 12'hB94, // reserved - CSR_MHPM_COUNTER_21H = 12'hB95, // reserved - CSR_MHPM_COUNTER_22H = 12'hB96, // reserved - CSR_MHPM_COUNTER_23H = 12'hB97, // reserved - CSR_MHPM_COUNTER_24H = 12'hB98, // reserved - CSR_MHPM_COUNTER_25H = 12'hB99, // reserved - CSR_MHPM_COUNTER_26H = 12'hB9A, // reserved - CSR_MHPM_COUNTER_27H = 12'hB9B, // reserved - CSR_MHPM_COUNTER_28H = 12'hB9C, // reserved - CSR_MHPM_COUNTER_29H = 12'hB9D, // reserved - CSR_MHPM_COUNTER_30H = 12'hB9E, // reserved - CSR_MHPM_COUNTER_31H = 12'hB9F, // reserved - // Cache Control (platform specifc) - CSR_DCACHE = 12'h7C1, - CSR_ICACHE = 12'h7C0, - // Accelerator memory consistency (platform specific) - CSR_ACC_CONS = 12'h7C2, - // Triggers - CSR_TSELECT = 12'h7A0, - CSR_TDATA1 = 12'h7A1, - CSR_TDATA2 = 12'h7A2, - CSR_TDATA3 = 12'h7A3, - CSR_TINFO = 12'h7A4, - // Debug CSR - CSR_DCSR = 12'h7b0, - CSR_DPC = 12'h7b1, - CSR_DSCRATCH0 = 12'h7b2, // optional - CSR_DSCRATCH1 = 12'h7b3, // optional - // Counters and Timers (User Mode - R/O Shadows) - CSR_CYCLE = 12'hC00, - CSR_CYCLEH = 12'hC80, - CSR_TIME = 12'hC01, - CSR_TIMEH = 12'hC81, - CSR_INSTRET = 12'hC02, - CSR_INSTRETH = 12'hC82, - // Performance counters (User Mode - R/O Shadows) - CSR_HPM_COUNTER_3 = 12'hC03, - CSR_HPM_COUNTER_4 = 12'hC04, - CSR_HPM_COUNTER_5 = 12'hC05, - CSR_HPM_COUNTER_6 = 12'hC06, - CSR_HPM_COUNTER_7 = 12'hC07, - CSR_HPM_COUNTER_8 = 12'hC08, - CSR_HPM_COUNTER_9 = 12'hC09, // reserved - CSR_HPM_COUNTER_10 = 12'hC0A, // reserved - CSR_HPM_COUNTER_11 = 12'hC0B, // reserved - CSR_HPM_COUNTER_12 = 12'hC0C, // reserved - CSR_HPM_COUNTER_13 = 12'hC0D, // reserved - CSR_HPM_COUNTER_14 = 12'hC0E, // reserved - CSR_HPM_COUNTER_15 = 12'hC0F, // reserved - CSR_HPM_COUNTER_16 = 12'hC10, // reserved - CSR_HPM_COUNTER_17 = 12'hC11, // reserved - CSR_HPM_COUNTER_18 = 12'hC12, // reserved - CSR_HPM_COUNTER_19 = 12'hC13, // reserved - CSR_HPM_COUNTER_20 = 12'hC14, // reserved - CSR_HPM_COUNTER_21 = 12'hC15, // reserved - CSR_HPM_COUNTER_22 = 12'hC16, // reserved - CSR_HPM_COUNTER_23 = 12'hC17, // reserved - CSR_HPM_COUNTER_24 = 12'hC18, // reserved - CSR_HPM_COUNTER_25 = 12'hC19, // reserved - CSR_HPM_COUNTER_26 = 12'hC1A, // reserved - CSR_HPM_COUNTER_27 = 12'hC1B, // reserved - CSR_HPM_COUNTER_28 = 12'hC1C, // reserved - CSR_HPM_COUNTER_29 = 12'hC1D, // reserved - CSR_HPM_COUNTER_30 = 12'hC1E, // reserved - CSR_HPM_COUNTER_31 = 12'hC1F, // reserved - CSR_HPM_COUNTER_3H = 12'hC83, - CSR_HPM_COUNTER_4H = 12'hC84, - CSR_HPM_COUNTER_5H = 12'hC85, - CSR_HPM_COUNTER_6H = 12'hC86, - CSR_HPM_COUNTER_7H = 12'hC87, - CSR_HPM_COUNTER_8H = 12'hC88, - CSR_HPM_COUNTER_9H = 12'hC89, // reserved - CSR_HPM_COUNTER_10H = 12'hC8A, // reserved - CSR_HPM_COUNTER_11H = 12'hC8B, // reserved - CSR_HPM_COUNTER_12H = 12'hC8C, // reserved - CSR_HPM_COUNTER_13H = 12'hC8D, // reserved - CSR_HPM_COUNTER_14H = 12'hC8E, // reserved - CSR_HPM_COUNTER_15H = 12'hC8F, // reserved - CSR_HPM_COUNTER_16H = 12'hC90, // reserved - CSR_HPM_COUNTER_17H = 12'hC91, // reserved - CSR_HPM_COUNTER_18H = 12'hC92, // reserved - CSR_HPM_COUNTER_19H = 12'hC93, // reserved - CSR_HPM_COUNTER_20H = 12'hC94, // reserved - CSR_HPM_COUNTER_21H = 12'hC95, // reserved - CSR_HPM_COUNTER_22H = 12'hC96, // reserved - CSR_HPM_COUNTER_23H = 12'hC97, // reserved - CSR_HPM_COUNTER_24H = 12'hC98, // reserved - CSR_HPM_COUNTER_25H = 12'hC99, // reserved - CSR_HPM_COUNTER_26H = 12'hC9A, // reserved - CSR_HPM_COUNTER_27H = 12'hC9B, // reserved - CSR_HPM_COUNTER_28H = 12'hC9C, // reserved - CSR_HPM_COUNTER_29H = 12'hC9D, // reserved - CSR_HPM_COUNTER_30H = 12'hC9E, // reserved - CSR_HPM_COUNTER_31H = 12'hC9F // reserved - } csr_reg_t; - - localparam logic [63:0] SSTATUS_UIE = 'h00000001; - localparam logic [63:0] SSTATUS_SIE = 'h00000002; - localparam logic [63:0] SSTATUS_SPIE = 'h00000020; - localparam logic [63:0] SSTATUS_SPP = 'h00000100; - localparam logic [63:0] SSTATUS_FS = 'h00006000; - localparam logic [63:0] SSTATUS_XS = 'h00018000; - localparam logic [63:0] SSTATUS_SUM = 'h00040000; - localparam logic [63:0] SSTATUS_MXR = 'h00080000; - localparam logic [63:0] SSTATUS_UPIE = 'h00000010; - localparam logic [63:0] SSTATUS_UXL = 64'h0000000300000000; - localparam logic [63:0] SSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; - - localparam logic [63:0] MSTATUS_UIE = 'h00000001; - localparam logic [63:0] MSTATUS_SIE = 'h00000002; - localparam logic [63:0] MSTATUS_HIE = 'h00000004; - localparam logic [63:0] MSTATUS_MIE = 'h00000008; - localparam logic [63:0] MSTATUS_UPIE = 'h00000010; - localparam logic [63:0] MSTATUS_SPIE = 'h00000020; - localparam logic [63:0] MSTATUS_HPIE = 'h00000040; - localparam logic [63:0] MSTATUS_MPIE = 'h00000080; - localparam logic [63:0] MSTATUS_SPP = 'h00000100; - localparam logic [63:0] MSTATUS_HPP = 'h00000600; - localparam logic [63:0] MSTATUS_MPP = 'h00001800; - localparam logic [63:0] MSTATUS_FS = 'h00006000; - localparam logic [63:0] MSTATUS_XS = 'h00018000; - localparam logic [63:0] MSTATUS_MPRV = 'h00020000; - localparam logic [63:0] MSTATUS_SUM = 'h00040000; - localparam logic [63:0] MSTATUS_MXR = 'h00080000; - localparam logic [63:0] MSTATUS_TVM = 'h00100000; - localparam logic [63:0] MSTATUS_TW = 'h00200000; - localparam logic [63:0] MSTATUS_TSR = 'h00400000; - localparam logic [63:0] MSTATUS_UXL = {30'h0000000, IS_XLEN64, IS_XLEN64, 32'h00000000}; - localparam logic [63:0] MSTATUS_SXL = {28'h0000000, IS_XLEN64, IS_XLEN64, 34'h00000000}; - localparam logic [63:0] MSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; - - typedef enum logic [2:0] { - CSRRW = 3'h1, - CSRRS = 3'h2, - CSRRC = 3'h3, - CSRRWI = 3'h5, - CSRRSI = 3'h6, - CSRRCI = 3'h7 - } csr_op_t; - - // decoded CSR address - typedef struct packed { - logic [1:0] rw; - priv_lvl_t priv_lvl; - logic [7:0] address; - } csr_addr_t; - - typedef union packed { - csr_reg_t address; - csr_addr_t csr_decode; - } csr_t; - - // Floating-Point control and status register (32-bit!) - typedef struct packed { - logic [31:15] reserved; // reserved for L extension, return 0 otherwise - logic [6:0] fprec; // div/sqrt precision control - logic [2:0] frm; // float rounding mode - logic [4:0] fflags; // float exception flags - } fcsr_t; - - // PMP - typedef enum logic [1:0] { - OFF = 2'b00, - TOR = 2'b01, - NA4 = 2'b10, - NAPOT = 2'b11 - } pmp_addr_mode_t; - - // PMP Access Type - typedef enum logic [2:0] { - ACCESS_NONE = 3'b000, - ACCESS_READ = 3'b001, - ACCESS_WRITE = 3'b010, - ACCESS_EXEC = 3'b100 - } pmp_access_t; - - typedef struct packed { - logic x; - logic w; - logic r; - } pmpcfg_access_t; - - // packed struct of a PMP configuration register (8bit) - typedef struct packed { - logic locked; // lock this configuration - logic [1:0] reserved; - pmp_addr_mode_t addr_mode; // Off, TOR, NA4, NAPOT - pmpcfg_access_t access_type; - } pmpcfg_t; - - // ----- - // Debug - // ----- - typedef struct packed { - logic [31:28] xdebugver; - logic [27:16] zero2; - logic ebreakm; - logic zero1; - logic ebreaks; - logic ebreaku; - logic stepie; - logic stopcount; - logic stoptime; - logic [8:6] cause; - logic zero0; - logic mprven; - logic nmip; - logic step; - priv_lvl_t prv; - } dcsr_t; - - // Instruction Generation *incomplete* - function automatic logic [31:0] jal(logic [4:0] rd, logic [20:0] imm); - // OpCode Jal - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; - endfunction - - function automatic logic [31:0] jalr(logic [4:0] rd, logic [4:0] rs1, logic [11:0] offset); - // OpCode Jal - return {offset[11:0], rs1, 3'b0, rd, 7'h67}; - endfunction - - function automatic logic [31:0] andi(logic [4:0] rd, logic [4:0] rs1, logic [11:0] imm); - // OpCode andi - return {imm[11:0], rs1, 3'h7, rd, 7'h13}; - endfunction - - function automatic logic [31:0] slli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt); - // OpCode slli - return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; - endfunction - - function automatic logic [31:0] srli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt); - // OpCode srli - return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; - endfunction - - function automatic logic [31:0] load(logic [2:0] size, logic [4:0] dest, logic [4:0] base, - logic [11:0] offset); - // OpCode Load - return {offset[11:0], base, size, dest, 7'h03}; - endfunction - - function automatic logic [31:0] auipc(logic [4:0] rd, logic [20:0] imm); - // OpCode Auipc - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; - endfunction - - function automatic logic [31:0] store(logic [2:0] size, logic [4:0] src, logic [4:0] base, - logic [11:0] offset); - // OpCode Store - return {offset[11:5], src, base, size, offset[4:0], 7'h23}; - endfunction - - function automatic logic [31:0] float_load(logic [2:0] size, logic [4:0] dest, logic [4:0] base, - logic [11:0] offset); - // OpCode Load - return {offset[11:0], base, size, dest, 7'b00_001_11}; - endfunction - - function automatic logic [31:0] float_store(logic [2:0] size, logic [4:0] src, logic [4:0] base, - logic [11:0] offset); - // OpCode Store - return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; - endfunction - - function automatic logic [31:0] csrw(csr_reg_t csr, logic [4:0] rs1); - // CSRRW, rd, OpCode System - return {csr, rs1, 3'h1, 5'h0, 7'h73}; - endfunction - - function automatic logic [31:0] csrr(csr_reg_t csr, logic [4:0] dest); - // rs1, CSRRS, rd, OpCode System - return {csr, 5'h0, 3'h2, dest, 7'h73}; - endfunction - - function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, - logic [11:0] offset); - // OpCode Branch - return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; - endfunction - - function automatic logic [31:0] ebreak(); - return 32'h00100073; - endfunction - - function automatic logic [31:0] wfi(); - return 32'h10500073; - endfunction - - function automatic logic [31:0] nop(); - return 32'h00000013; - endfunction - - function automatic logic [31:0] illegal(); - return 32'h00000000; - endfunction - - - // trace log compatible to spikes commit log feature - // pragma translate_off - function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr, - logic [4:0] rd, logic [63:0] result, logic rd_fpr); - string rd_s; - string instr_word; - - automatic string rf_s = rd_fpr ? "f" : "x"; - - if (instr[1:0] != 2'b11) begin - instr_word = $sformatf("(0x%h)", instr[15:0]); - end else begin - instr_word = $sformatf("(0x%h)", instr); - end - - if (rd < 10) rd_s = $sformatf("%s %0d", rf_s, rd); - else rd_s = $sformatf("%s%0d", rf_s, rd); - - if (rd_fpr || rd != 0) begin - // 0 0x0000000080000118 (0xeecf8f93) x31 0x0000000080004000 - return $sformatf("%d 0x%h %s %s 0x%h\n", priv_lvl, pc, instr_word, rd_s, result); - end else begin - // 0 0x000000008000019c (0x0040006f) - return $sformatf("%d 0x%h %s\n", priv_lvl, pc, instr_word); - end - endfunction - - typedef struct { - byte priv; - longint unsigned pc; - byte is_fp; - byte rd; - longint unsigned data; - int unsigned instr; - byte was_exception; - } commit_log_t; - // pragma translate_on + } vm_mode_t; + + // Warning: When using STD_CACHE, configuration must be PLEN=56 and VLEN=64 + // Warning: VLEN must be superior or equal to PLEN + localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length + localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length + + localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; + localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; + localparam ModeW = (XLEN == 32) ? 1 : 4; + localparam ASIDW = (XLEN == 32) ? 9 : 16; + localparam PPNW = (XLEN == 32) ? 22 : 44; + localparam vm_mode_t MODE_SV = (XLEN == 32) ? ModeSv32 : ModeSv39; + localparam SV = (MODE_SV == ModeSv32) ? 32 : 39; + localparam VPN2 = (VLEN-31 < 8) ? VLEN-31 : 8; + localparam XLEN_ALIGN_BYTES = $clog2(XLEN/8); + + typedef logic [XLEN-1:0] xlen_t; + + // -------------------- + // Privilege Spec + // -------------------- + typedef enum logic[1:0] { + PRIV_LVL_M = 2'b11, + PRIV_LVL_S = 2'b01, + PRIV_LVL_U = 2'b00 + } priv_lvl_t; + + // type which holds xlen + typedef enum logic [1:0] { + XLEN_32 = 2'b01, + XLEN_64 = 2'b10, + XLEN_128 = 2'b11 + } xlen_e; + + typedef enum logic [1:0] { + Off = 2'b00, + Initial = 2'b01, + Clean = 2'b10, + Dirty = 2'b11 + } xs_t; + + typedef struct packed { + logic sd; // signal dirty state - read-only + logic [62:34] wpri6; // writes preserved reads ignored + xlen_e uxl; // variable user mode xlen - hardwired to zero + logic [12:0] wpri5; // writes preserved reads ignored + logic mxr; // make executable readable + logic sum; // permit supervisor user memory access + logic wpri4; // writes preserved reads ignored + xs_t xs; // extension register - hardwired to zero + xs_t fs; // floating point extension register + logic [1:0] wpri3; // writes preserved reads ignored + xs_t vs; // vector extension register + logic spp; // holds the previous privilege mode up to supervisor + logic wpri2; // writes preserved reads ignored + logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) + logic spie; // supervisor interrupts enable bit active prior to trap + logic [1:0] wpri1; // writes preserved reads ignored + logic sie; // supervisor interrupts enable + logic wpri0; // writes preserved reads ignored + } sstatus_rv_t; + + typedef struct packed { + logic sd; // signal dirty state - read-only + logic [62:36] wpri4; // writes preserved reads ignored + xlen_e sxl; // variable supervisor mode xlen - hardwired to zero + xlen_e uxl; // variable user mode xlen - hardwired to zero + logic [8:0] wpri3; // writes preserved reads ignored + logic tsr; // trap sret + logic tw; // time wait + logic tvm; // trap virtual memory + logic mxr; // make executable readable + logic sum; // permit supervisor user memory access + logic mprv; // modify privilege - privilege level for ld/st + xs_t xs; // extension register - hardwired to zero + xs_t fs; // floating point extension register + priv_lvl_t mpp; // holds the previous privilege mode up to machine + xs_t vs; // vector extension register + logic spp; // holds the previous privilege mode up to supervisor + logic mpie; // machine interrupts enable bit active prior to trap + logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) + logic spie; // supervisor interrupts enable bit active prior to trap + logic wpri2; // writes preserved reads ignored + logic mie; // machine interrupts enable + logic wpri1; // writes preserved reads ignored + logic sie; // supervisor interrupts enable + logic wpri0; // writes preserved reads ignored + } mstatus_rv_t; + + typedef struct packed { + logic [ModeW-1:0] mode; + logic [ASIDW-1:0] asid; + logic [PPNW-1:0] ppn; + } satp_t; + + // -------------------- + // Instruction Types + // -------------------- + typedef struct packed { + logic [31:25] funct7; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } rtype_t; + + typedef struct packed { + logic [31:27] rs3; + logic [26:25] funct2; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } r4type_t; + + typedef struct packed { + logic [31:27] funct5; + logic [26:25] fmt; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] rm; + logic [11:7] rd; + logic [6:0] opcode; + } rftype_t; // floating-point + + typedef struct packed { + logic [31:30] funct2; + logic [29:25] vecfltop; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:14] repl; + logic [13:12] vfmt; + logic [11:7] rd; + logic [6:0] opcode; + } rvftype_t; // vectorial floating-point + + typedef struct packed { + logic [31:20] imm; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } itype_t; + + typedef struct packed { + logic [31:25] imm; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] imm0; + logic [6:0] opcode; + } stype_t; + + typedef struct packed { + logic [31:12] imm; + logic [11:7] rd; + logic [6:0] opcode; + } utype_t; + + // atomic instructions + typedef struct packed { + logic [31:27] funct5; + logic aq; + logic rl; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } atype_t; + + typedef union packed { + logic [31:0] instr; + rtype_t rtype; + r4type_t r4type; + rftype_t rftype; + rvftype_t rvftype; + itype_t itype; + stype_t stype; + utype_t utype; + atype_t atype; + } instruction_t; + + // -------------------- + // Opcodes + // -------------------- + // RV32/64G listings: + // Quadrant 0 + localparam OpcodeLoad = 7'b00_000_11; + localparam OpcodeLoadFp = 7'b00_001_11; + localparam OpcodeCustom0 = 7'b00_010_11; + localparam OpcodeMiscMem = 7'b00_011_11; + localparam OpcodeOpImm = 7'b00_100_11; + localparam OpcodeAuipc = 7'b00_101_11; + localparam OpcodeOpImm32 = 7'b00_110_11; + // Quadrant 1 + localparam OpcodeStore = 7'b01_000_11; + localparam OpcodeStoreFp = 7'b01_001_11; + localparam OpcodeCustom1 = 7'b01_010_11; + localparam OpcodeAmo = 7'b01_011_11; + localparam OpcodeOp = 7'b01_100_11; + localparam OpcodeLui = 7'b01_101_11; + localparam OpcodeOp32 = 7'b01_110_11; + // Quadrant 2 + localparam OpcodeMadd = 7'b10_000_11; + localparam OpcodeMsub = 7'b10_001_11; + localparam OpcodeNmsub = 7'b10_010_11; + localparam OpcodeNmadd = 7'b10_011_11; + localparam OpcodeOpFp = 7'b10_100_11; + localparam OpcodeVec = 7'b10_101_11; + localparam OpcodeCustom2 = 7'b10_110_11; + // Quadrant 3 + localparam OpcodeBranch = 7'b11_000_11; + localparam OpcodeJalr = 7'b11_001_11; + localparam OpcodeRsrvd2 = 7'b11_010_11; + localparam OpcodeJal = 7'b11_011_11; + localparam OpcodeSystem = 7'b11_100_11; + localparam OpcodeRsrvd3 = 7'b11_101_11; + localparam OpcodeCustom3 = 7'b11_110_11; + + // RV64C/RV32C listings: + // Quadrant 0 + localparam OpcodeC0 = 2'b00; + localparam OpcodeC0Addi4spn = 3'b000; + localparam OpcodeC0Fld = 3'b001; + localparam OpcodeC0Lw = 3'b010; + localparam OpcodeC0Ld = 3'b011; + localparam OpcodeC0Rsrvd = 3'b100; + localparam OpcodeC0Fsd = 3'b101; + localparam OpcodeC0Sw = 3'b110; + localparam OpcodeC0Sd = 3'b111; + // Quadrant 1 + localparam OpcodeC1 = 2'b01; + localparam OpcodeC1Addi = 3'b000; + localparam OpcodeC1Addiw = 3'b001; //for RV64I only + localparam OpcodeC1Jal = 3'b001; //for RV32I only + localparam OpcodeC1Li = 3'b010; + localparam OpcodeC1LuiAddi16sp = 3'b011; + localparam OpcodeC1MiscAlu = 3'b100; + localparam OpcodeC1J = 3'b101; + localparam OpcodeC1Beqz = 3'b110; + localparam OpcodeC1Bnez = 3'b111; + // Quadrant 2 + localparam OpcodeC2 = 2'b10; + localparam OpcodeC2Slli = 3'b000; + localparam OpcodeC2Fldsp = 3'b001; + localparam OpcodeC2Lwsp = 3'b010; + localparam OpcodeC2Ldsp = 3'b011; + localparam OpcodeC2JalrMvAdd = 3'b100; + localparam OpcodeC2Fsdsp = 3'b101; + localparam OpcodeC2Swsp = 3'b110; + localparam OpcodeC2Sdsp = 3'b111; + + // ---------------------- + // Virtual Memory + // ---------------------- + // memory management, pte for sv39 + typedef struct packed { + logic [9:0] reserved; + logic [44-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; + } pte_t; + + // memory management, pte for sv32 + typedef struct packed { + logic [22-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; + } pte_sv32_t; + + // memory management, pte for cva6 + typedef struct packed { + logic [PPNW-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; + } pte_cva6_t; + + // ---------------------- + // Exception Cause Codes + // ---------------------- + localparam logic [XLEN-1:0] INSTR_ADDR_MISALIGNED = 0; + localparam logic [XLEN-1:0] INSTR_ACCESS_FAULT = 1; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ILLEGAL_INSTR = 2; + localparam logic [XLEN-1:0] BREAKPOINT = 3; + localparam logic [XLEN-1:0] LD_ADDR_MISALIGNED = 4; + localparam logic [XLEN-1:0] LD_ACCESS_FAULT = 5; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ST_ADDR_MISALIGNED = 6; + localparam logic [XLEN-1:0] ST_ACCESS_FAULT = 7; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode + localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from supervisor mode + localparam logic [XLEN-1:0] ENV_CALL_MMODE = 11; // environment call from machine mode + localparam logic [XLEN-1:0] INSTR_PAGE_FAULT = 12; // Instruction page fault + localparam logic [XLEN-1:0] LOAD_PAGE_FAULT = 13; // Load page fault + localparam logic [XLEN-1:0] STORE_PAGE_FAULT = 15; // Store page fault + localparam logic [XLEN-1:0] DEBUG_REQUEST = 24; // Debug request + + localparam int unsigned IRQ_S_SOFT = 1; + localparam int unsigned IRQ_M_SOFT = 3; + localparam int unsigned IRQ_S_TIMER = 5; + localparam int unsigned IRQ_M_TIMER = 7; + localparam int unsigned IRQ_S_EXT = 9; + localparam int unsigned IRQ_M_EXT = 11; + + localparam logic [XLEN-1:0] MIP_SSIP = 1 << IRQ_S_SOFT; + localparam logic [XLEN-1:0] MIP_MSIP = 1 << IRQ_M_SOFT; + localparam logic [XLEN-1:0] MIP_STIP = 1 << IRQ_S_TIMER; + localparam logic [XLEN-1:0] MIP_MTIP = 1 << IRQ_M_TIMER; + localparam logic [XLEN-1:0] MIP_SEIP = 1 << IRQ_S_EXT; + localparam logic [XLEN-1:0] MIP_MEIP = 1 << IRQ_M_EXT; + + localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_SOFT); + localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_SOFT); + localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_TIMER); + localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_TIMER); + localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_EXT); + localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_EXT); + + // ----- + // CSRs + // ----- + typedef enum logic [11:0] { + // Floating-Point CSRs + CSR_FFLAGS = 12'h001, + CSR_FRM = 12'h002, + CSR_FCSR = 12'h003, + CSR_FTRAN = 12'h800, + // Vector CSRs + CSR_VSTART = 12'h008, + CSR_VXSAT = 12'h009, + CSR_VXRM = 12'h00A, + CSR_VCSR = 12'h00F, + CSR_VL = 12'hC20, + CSR_VTYPE = 12'hC21, + CSR_VLENB = 12'hC22, + // Supervisor Mode CSRs + CSR_SSTATUS = 12'h100, + CSR_SIE = 12'h104, + CSR_STVEC = 12'h105, + CSR_SCOUNTEREN = 12'h106, + CSR_SSCRATCH = 12'h140, + CSR_SEPC = 12'h141, + CSR_SCAUSE = 12'h142, + CSR_STVAL = 12'h143, + CSR_SIP = 12'h144, + CSR_SATP = 12'h180, + // Machine Mode CSRs + CSR_MSTATUS = 12'h300, + CSR_MISA = 12'h301, + CSR_MEDELEG = 12'h302, + CSR_MIDELEG = 12'h303, + CSR_MIE = 12'h304, + CSR_MTVEC = 12'h305, + CSR_MCOUNTEREN = 12'h306, + CSR_MCOUNTINHIBIT = 12'h320, + CSR_MHPM_EVENT_3 = 12'h323, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_4 = 12'h324, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_5 = 12'h325, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_6 = 12'h326, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_7 = 12'h327, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_8 = 12'h328, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_9 = 12'h329, //Reserved + CSR_MHPM_EVENT_10 = 12'h32A, //Reserved + CSR_MHPM_EVENT_11 = 12'h32B, //Reserved + CSR_MHPM_EVENT_12 = 12'h32C, //Reserved + CSR_MHPM_EVENT_13 = 12'h32D, //Reserved + CSR_MHPM_EVENT_14 = 12'h32E, //Reserved + CSR_MHPM_EVENT_15 = 12'h32F, //Reserved + CSR_MHPM_EVENT_16 = 12'h330, //Reserved + CSR_MHPM_EVENT_17 = 12'h331, //Reserved + CSR_MHPM_EVENT_18 = 12'h332, //Reserved + CSR_MHPM_EVENT_19 = 12'h333, //Reserved + CSR_MHPM_EVENT_20 = 12'h334, //Reserved + CSR_MHPM_EVENT_21 = 12'h335, //Reserved + CSR_MHPM_EVENT_22 = 12'h336, //Reserved + CSR_MHPM_EVENT_23 = 12'h337, //Reserved + CSR_MHPM_EVENT_24 = 12'h338, //Reserved + CSR_MHPM_EVENT_25 = 12'h339, //Reserved + CSR_MHPM_EVENT_26 = 12'h33A, //Reserved + CSR_MHPM_EVENT_27 = 12'h33B, //Reserved + CSR_MHPM_EVENT_28 = 12'h33C, //Reserved + CSR_MHPM_EVENT_29 = 12'h33D, //Reserved + CSR_MHPM_EVENT_30 = 12'h33E, //Reserved + CSR_MHPM_EVENT_31 = 12'h33F, //Reserved + CSR_MSCRATCH = 12'h340, + CSR_MEPC = 12'h341, + CSR_MCAUSE = 12'h342, + CSR_MTVAL = 12'h343, + CSR_MIP = 12'h344, + CSR_PMPCFG0 = 12'h3A0, + CSR_PMPCFG1 = 12'h3A1, + CSR_PMPCFG2 = 12'h3A2, + CSR_PMPCFG3 = 12'h3A3, + CSR_PMPADDR0 = 12'h3B0, + CSR_PMPADDR1 = 12'h3B1, + CSR_PMPADDR2 = 12'h3B2, + CSR_PMPADDR3 = 12'h3B3, + CSR_PMPADDR4 = 12'h3B4, + CSR_PMPADDR5 = 12'h3B5, + CSR_PMPADDR6 = 12'h3B6, + CSR_PMPADDR7 = 12'h3B7, + CSR_PMPADDR8 = 12'h3B8, + CSR_PMPADDR9 = 12'h3B9, + CSR_PMPADDR10 = 12'h3BA, + CSR_PMPADDR11 = 12'h3BB, + CSR_PMPADDR12 = 12'h3BC, + CSR_PMPADDR13 = 12'h3BD, + CSR_PMPADDR14 = 12'h3BE, + CSR_PMPADDR15 = 12'h3BF, + CSR_MVENDORID = 12'hF11, + CSR_MARCHID = 12'hF12, + CSR_MIMPID = 12'hF13, + CSR_MHARTID = 12'hF14, + CSR_MCYCLE = 12'hB00, + CSR_MCYCLEH = 12'hB80, + CSR_MINSTRET = 12'hB02, + CSR_MINSTRETH = 12'hB82, + //Performance Counters + CSR_MHPM_COUNTER_3 = 12'hB03, + CSR_MHPM_COUNTER_4 = 12'hB04, + CSR_MHPM_COUNTER_5 = 12'hB05, + CSR_MHPM_COUNTER_6 = 12'hB06, + CSR_MHPM_COUNTER_7 = 12'hB07, + CSR_MHPM_COUNTER_8 = 12'hB08, + CSR_MHPM_COUNTER_9 = 12'hB09, // reserved + CSR_MHPM_COUNTER_10 = 12'hB0A, // reserved + CSR_MHPM_COUNTER_11 = 12'hB0B, // reserved + CSR_MHPM_COUNTER_12 = 12'hB0C, // reserved + CSR_MHPM_COUNTER_13 = 12'hB0D, // reserved + CSR_MHPM_COUNTER_14 = 12'hB0E, // reserved + CSR_MHPM_COUNTER_15 = 12'hB0F, // reserved + CSR_MHPM_COUNTER_16 = 12'hB10, // reserved + CSR_MHPM_COUNTER_17 = 12'hB11, // reserved + CSR_MHPM_COUNTER_18 = 12'hB12, // reserved + CSR_MHPM_COUNTER_19 = 12'hB13, // reserved + CSR_MHPM_COUNTER_20 = 12'hB14, // reserved + CSR_MHPM_COUNTER_21 = 12'hB15, // reserved + CSR_MHPM_COUNTER_22 = 12'hB16, // reserved + CSR_MHPM_COUNTER_23 = 12'hB17, // reserved + CSR_MHPM_COUNTER_24 = 12'hB18, // reserved + CSR_MHPM_COUNTER_25 = 12'hB19, // reserved + CSR_MHPM_COUNTER_26 = 12'hB1A, // reserved + CSR_MHPM_COUNTER_27 = 12'hB1B, // reserved + CSR_MHPM_COUNTER_28 = 12'hB1C, // reserved + CSR_MHPM_COUNTER_29 = 12'hB1D, // reserved + CSR_MHPM_COUNTER_30 = 12'hB1E, // reserved + CSR_MHPM_COUNTER_31 = 12'hB1F, // reserved + CSR_MHPM_COUNTER_3H = 12'hB83, + CSR_MHPM_COUNTER_4H = 12'hB84, + CSR_MHPM_COUNTER_5H = 12'hB85, + CSR_MHPM_COUNTER_6H = 12'hB86, + CSR_MHPM_COUNTER_7H = 12'hB87, + CSR_MHPM_COUNTER_8H = 12'hB88, + CSR_MHPM_COUNTER_9H = 12'hB89, // reserved + CSR_MHPM_COUNTER_10H = 12'hB8A, // reserved + CSR_MHPM_COUNTER_11H = 12'hB8B, // reserved + CSR_MHPM_COUNTER_12H = 12'hB8C, // reserved + CSR_MHPM_COUNTER_13H = 12'hB8D, // reserved + CSR_MHPM_COUNTER_14H = 12'hB8E, // reserved + CSR_MHPM_COUNTER_15H = 12'hB8F, // reserved + CSR_MHPM_COUNTER_16H = 12'hB90, // reserved + CSR_MHPM_COUNTER_17H = 12'hB91, // reserved + CSR_MHPM_COUNTER_18H = 12'hB92, // reserved + CSR_MHPM_COUNTER_19H = 12'hB93, // reserved + CSR_MHPM_COUNTER_20H = 12'hB94, // reserved + CSR_MHPM_COUNTER_21H = 12'hB95, // reserved + CSR_MHPM_COUNTER_22H = 12'hB96, // reserved + CSR_MHPM_COUNTER_23H = 12'hB97, // reserved + CSR_MHPM_COUNTER_24H = 12'hB98, // reserved + CSR_MHPM_COUNTER_25H = 12'hB99, // reserved + CSR_MHPM_COUNTER_26H = 12'hB9A, // reserved + CSR_MHPM_COUNTER_27H = 12'hB9B, // reserved + CSR_MHPM_COUNTER_28H = 12'hB9C, // reserved + CSR_MHPM_COUNTER_29H = 12'hB9D, // reserved + CSR_MHPM_COUNTER_30H = 12'hB9E, // reserved + CSR_MHPM_COUNTER_31H = 12'hB9F, // reserved + // Cache Control (platform specifc) + CSR_DCACHE = 12'h7C1, + CSR_ICACHE = 12'h7C0, + // Accelerator memory consistency (platform specific) + CSR_ACC_CONS = 12'h7C2, + // Triggers + CSR_TSELECT = 12'h7A0, + CSR_TDATA1 = 12'h7A1, + CSR_TDATA2 = 12'h7A2, + CSR_TDATA3 = 12'h7A3, + CSR_TINFO = 12'h7A4, + // Debug CSR + CSR_DCSR = 12'h7b0, + CSR_DPC = 12'h7b1, + CSR_DSCRATCH0 = 12'h7b2, // optional + CSR_DSCRATCH1 = 12'h7b3, // optional + // Counters and Timers (User Mode - R/O Shadows) + CSR_CYCLE = 12'hC00, + CSR_CYCLEH = 12'hC80, + CSR_TIME = 12'hC01, + CSR_TIMEH = 12'hC81, + CSR_INSTRET = 12'hC02, + CSR_INSTRETH = 12'hC82, + // Performance counters (User Mode - R/O Shadows) + CSR_HPM_COUNTER_3 = 12'hC03, + CSR_HPM_COUNTER_4 = 12'hC04, + CSR_HPM_COUNTER_5 = 12'hC05, + CSR_HPM_COUNTER_6 = 12'hC06, + CSR_HPM_COUNTER_7 = 12'hC07, + CSR_HPM_COUNTER_8 = 12'hC08, + CSR_HPM_COUNTER_9 = 12'hC09, // reserved + CSR_HPM_COUNTER_10 = 12'hC0A, // reserved + CSR_HPM_COUNTER_11 = 12'hC0B, // reserved + CSR_HPM_COUNTER_12 = 12'hC0C, // reserved + CSR_HPM_COUNTER_13 = 12'hC0D, // reserved + CSR_HPM_COUNTER_14 = 12'hC0E, // reserved + CSR_HPM_COUNTER_15 = 12'hC0F, // reserved + CSR_HPM_COUNTER_16 = 12'hC10, // reserved + CSR_HPM_COUNTER_17 = 12'hC11, // reserved + CSR_HPM_COUNTER_18 = 12'hC12, // reserved + CSR_HPM_COUNTER_19 = 12'hC13, // reserved + CSR_HPM_COUNTER_20 = 12'hC14, // reserved + CSR_HPM_COUNTER_21 = 12'hC15, // reserved + CSR_HPM_COUNTER_22 = 12'hC16, // reserved + CSR_HPM_COUNTER_23 = 12'hC17, // reserved + CSR_HPM_COUNTER_24 = 12'hC18, // reserved + CSR_HPM_COUNTER_25 = 12'hC19, // reserved + CSR_HPM_COUNTER_26 = 12'hC1A, // reserved + CSR_HPM_COUNTER_27 = 12'hC1B, // reserved + CSR_HPM_COUNTER_28 = 12'hC1C, // reserved + CSR_HPM_COUNTER_29 = 12'hC1D, // reserved + CSR_HPM_COUNTER_30 = 12'hC1E, // reserved + CSR_HPM_COUNTER_31 = 12'hC1F, // reserved + CSR_HPM_COUNTER_3H = 12'hC83, + CSR_HPM_COUNTER_4H = 12'hC84, + CSR_HPM_COUNTER_5H = 12'hC85, + CSR_HPM_COUNTER_6H = 12'hC86, + CSR_HPM_COUNTER_7H = 12'hC87, + CSR_HPM_COUNTER_8H = 12'hC88, + CSR_HPM_COUNTER_9H = 12'hC89, // reserved + CSR_HPM_COUNTER_10H = 12'hC8A, // reserved + CSR_HPM_COUNTER_11H = 12'hC8B, // reserved + CSR_HPM_COUNTER_12H = 12'hC8C, // reserved + CSR_HPM_COUNTER_13H = 12'hC8D, // reserved + CSR_HPM_COUNTER_14H = 12'hC8E, // reserved + CSR_HPM_COUNTER_15H = 12'hC8F, // reserved + CSR_HPM_COUNTER_16H = 12'hC90, // reserved + CSR_HPM_COUNTER_17H = 12'hC91, // reserved + CSR_HPM_COUNTER_18H = 12'hC92, // reserved + CSR_HPM_COUNTER_19H = 12'hC93, // reserved + CSR_HPM_COUNTER_20H = 12'hC94, // reserved + CSR_HPM_COUNTER_21H = 12'hC95, // reserved + CSR_HPM_COUNTER_22H = 12'hC96, // reserved + CSR_HPM_COUNTER_23H = 12'hC97, // reserved + CSR_HPM_COUNTER_24H = 12'hC98, // reserved + CSR_HPM_COUNTER_25H = 12'hC99, // reserved + CSR_HPM_COUNTER_26H = 12'hC9A, // reserved + CSR_HPM_COUNTER_27H = 12'hC9B, // reserved + CSR_HPM_COUNTER_28H = 12'hC9C, // reserved + CSR_HPM_COUNTER_29H = 12'hC9D, // reserved + CSR_HPM_COUNTER_30H = 12'hC9E, // reserved + CSR_HPM_COUNTER_31H = 12'hC9F // reserved + } csr_reg_t; + + localparam logic [63:0] SSTATUS_UIE = 'h00000001; + localparam logic [63:0] SSTATUS_SIE = 'h00000002; + localparam logic [63:0] SSTATUS_SPIE = 'h00000020; + localparam logic [63:0] SSTATUS_SPP = 'h00000100; + localparam logic [63:0] SSTATUS_FS = 'h00006000; + localparam logic [63:0] SSTATUS_XS = 'h00018000; + localparam logic [63:0] SSTATUS_SUM = 'h00040000; + localparam logic [63:0] SSTATUS_MXR = 'h00080000; + localparam logic [63:0] SSTATUS_UPIE = 'h00000010; + localparam logic [63:0] SSTATUS_UXL = 64'h0000000300000000; + localparam logic [63:0] SSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; + + localparam logic [63:0] MSTATUS_UIE = 'h00000001; + localparam logic [63:0] MSTATUS_SIE = 'h00000002; + localparam logic [63:0] MSTATUS_HIE = 'h00000004; + localparam logic [63:0] MSTATUS_MIE = 'h00000008; + localparam logic [63:0] MSTATUS_UPIE = 'h00000010; + localparam logic [63:0] MSTATUS_SPIE = 'h00000020; + localparam logic [63:0] MSTATUS_HPIE = 'h00000040; + localparam logic [63:0] MSTATUS_MPIE = 'h00000080; + localparam logic [63:0] MSTATUS_SPP = 'h00000100; + localparam logic [63:0] MSTATUS_HPP = 'h00000600; + localparam logic [63:0] MSTATUS_MPP = 'h00001800; + localparam logic [63:0] MSTATUS_FS = 'h00006000; + localparam logic [63:0] MSTATUS_XS = 'h00018000; + localparam logic [63:0] MSTATUS_MPRV = 'h00020000; + localparam logic [63:0] MSTATUS_SUM = 'h00040000; + localparam logic [63:0] MSTATUS_MXR = 'h00080000; + localparam logic [63:0] MSTATUS_TVM = 'h00100000; + localparam logic [63:0] MSTATUS_TW = 'h00200000; + localparam logic [63:0] MSTATUS_TSR = 'h00400000; + localparam logic [63:0] MSTATUS_UXL = {30'h0000000, IS_XLEN64, IS_XLEN64, 32'h00000000}; + localparam logic [63:0] MSTATUS_SXL = {28'h0000000, IS_XLEN64, IS_XLEN64, 34'h00000000}; + localparam logic [63:0] MSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; + + typedef enum logic [2:0] { + CSRRW = 3'h1, + CSRRS = 3'h2, + CSRRC = 3'h3, + CSRRWI = 3'h5, + CSRRSI = 3'h6, + CSRRCI = 3'h7 + } csr_op_t; + + // decoded CSR address + typedef struct packed { + logic [1:0] rw; + priv_lvl_t priv_lvl; + logic [7:0] address; + } csr_addr_t; + + typedef union packed { + csr_reg_t address; + csr_addr_t csr_decode; + } csr_t; + + // Floating-Point control and status register (32-bit!) + typedef struct packed { + logic [31:15] reserved; // reserved for L extension, return 0 otherwise + logic [6:0] fprec; // div/sqrt precision control + logic [2:0] frm; // float rounding mode + logic [4:0] fflags; // float exception flags + } fcsr_t; + + // PMP + typedef enum logic [1:0] { + OFF = 2'b00, + TOR = 2'b01, + NA4 = 2'b10, + NAPOT = 2'b11 + } pmp_addr_mode_t; + + // PMP Access Type + typedef enum logic [2:0] { + ACCESS_NONE = 3'b000, + ACCESS_READ = 3'b001, + ACCESS_WRITE = 3'b010, + ACCESS_EXEC = 3'b100 + } pmp_access_t; + + typedef struct packed { + logic x; + logic w; + logic r; + } pmpcfg_access_t; + + // packed struct of a PMP configuration register (8bit) + typedef struct packed { + logic locked; // lock this configuration + logic [1:0] reserved; + pmp_addr_mode_t addr_mode; // Off, TOR, NA4, NAPOT + pmpcfg_access_t access_type; + } pmpcfg_t; + + // ----- + // Debug + // ----- + typedef struct packed { + logic [31:28] xdebugver; + logic [27:16] zero2; + logic ebreakm; + logic zero1; + logic ebreaks; + logic ebreaku; + logic stepie; + logic stopcount; + logic stoptime; + logic [8:6] cause; + logic zero0; + logic mprven; + logic nmip; + logic step; + priv_lvl_t prv; + } dcsr_t; + + // Instruction Generation *incomplete* + function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm); + // OpCode Jal + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; + endfunction + + function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset); + // OpCode Jal + return {offset[11:0], rs1, 3'b0, rd, 7'h67}; + endfunction + + function automatic logic [31:0] andi (logic[4:0] rd, logic[4:0] rs1, logic [11:0] imm); + // OpCode andi + return {imm[11:0], rs1, 3'h7, rd, 7'h13}; + endfunction + + function automatic logic [31:0] slli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); + // OpCode slli + return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; + endfunction + + function automatic logic [31:0] srli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); + // OpCode srli + return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; + endfunction + + function automatic logic [31:0] load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'h03}; + endfunction + + function automatic logic [31:0] auipc (logic[4:0] rd, logic [20:0] imm); + // OpCode Auipc + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; + endfunction + + function automatic logic [31:0] store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'h23}; + endfunction + + function automatic logic [31:0] float_load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'b00_001_11}; + endfunction + + function automatic logic [31:0] float_store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; + endfunction + + function automatic logic [31:0] csrw (csr_reg_t csr, logic[4:0] rs1); + // CSRRW, rd, OpCode System + return {csr, rs1, 3'h1, 5'h0, 7'h73}; + endfunction + + function automatic logic [31:0] csrr (csr_reg_t csr, logic [4:0] dest); + // rs1, CSRRS, rd, OpCode System + return {csr, 5'h0, 3'h2, dest, 7'h73}; + endfunction + + function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, logic [11:0] offset); + // OpCode Branch + return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; + endfunction + + function automatic logic [31:0] ebreak (); + return 32'h00100073; + endfunction + + function automatic logic [31:0] wfi (); + return 32'h10500073; + endfunction + + function automatic logic [31:0] nop (); + return 32'h00000013; + endfunction + + function automatic logic [31:0] illegal (); + return 32'h00000000; + endfunction + + + // trace log compatible to spikes commit log feature + // pragma translate_off + function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr, logic [4:0] rd, logic [63:0] result, logic rd_fpr); + string rd_s; + string instr_word; + + automatic string rf_s = rd_fpr ? "f" : "x"; + + if (instr[1:0] != 2'b11) begin + instr_word = $sformatf("(0x%h)", instr[15:0]); + end else begin + instr_word = $sformatf("(0x%h)", instr); + end + + if (rd < 10) rd_s = $sformatf("%s %0d", rf_s, rd); + else rd_s = $sformatf("%s%0d", rf_s, rd); + + if (rd_fpr || rd != 0) begin + // 0 0x0000000080000118 (0xeecf8f93) x31 0x0000000080004000 + return $sformatf("%d 0x%h %s %s 0x%h\n", priv_lvl, pc, instr_word, rd_s, result); + end else begin + // 0 0x000000008000019c (0x0040006f) + return $sformatf("%d 0x%h %s\n", priv_lvl, pc, instr_word); + end + endfunction + + typedef struct { + byte priv; + longint unsigned pc; + byte is_fp; + byte rd; + longint unsigned data; + int unsigned instr; + byte was_exception; + } commit_log_t; + // pragma translate_on endpackage From 263486528fe335238baa72d2137fb1595db0f5d7 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 21 Nov 2023 21:28:44 +0100 Subject: [PATCH 04/64] undo last commit --- core/include/ariane_pkg.sv | 1896 ++++++++++++++++++------------------ core/include/riscv_pkg.sv | 1654 ++++++++++++++++--------------- 2 files changed, 1771 insertions(+), 1779 deletions(-) diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index 2e7a47b632..446f609a61 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -19,991 +19,989 @@ // this is needed to propagate the // configuration in case Ariane is // instantiated in OpenPiton - `ifdef PITON_ARIANE - `include "l15.tmp.h" +`ifdef PITON_ARIANE +`include "l15.tmp.h" `endif +/// This package contains `functions` and global defines for CVA6. +/// *Note*: There are some parameters here as well which will eventually be +/// moved out to favour a fully parameterizable core. package ariane_pkg; - // --------------- - // Global Config - // --------------- - // This is the new user config interface system. If you need to parameterize something - // within Ariane add a field here and assign a default value to the config. Please make - // sure to add a propper parameter check to the `check_cfg` function. - typedef struct packed { - int unsigned dummy; - } cva6_cfg_t; - - localparam cva6_cfg_t cva6_cfg_empty = { - 32'b0 - }; - - localparam NrMaxRules = 16; - typedef struct packed { - int RASDepth; - int BTBEntries; - int BHTEntries; - // PMAs - int unsigned NrNonIdempotentRules; // Number of non idempotent rules - logic [NrMaxRules-1:0][63:0] NonIdempotentAddrBase; // base which needs to match - logic [NrMaxRules-1:0][63:0] NonIdempotentLength; // bit mask which bits to consider when matching the rule - int unsigned NrExecuteRegionRules; // Number of regions which have execute property - logic [NrMaxRules-1:0][63:0] ExecuteRegionAddrBase; // base which needs to match - logic [NrMaxRules-1:0][63:0] ExecuteRegionLength; // bit mask which bits to consider when matching the rule - int unsigned NrCachedRegionRules; // Number of regions which have cached property - logic [NrMaxRules-1:0][63:0] CachedRegionAddrBase; // base which needs to match - logic [NrMaxRules-1:0][63:0] CachedRegionLength; // bit mask which bits to consider when matching the rule - // cache config - bit AxiCompliant; // set to 1 when using in conjunction with 64bit AXI bus adapter - bit SwapEndianess; // set to 1 to swap endianess inside L1.5 openpiton adapter - // - logic [63:0] DmBaseAddress; // offset of the debug module - int unsigned NrPMPEntries; // Number of PMP entries - } ariane_cfg_t; - - localparam ariane_cfg_t ArianeDefaultConfig = '{ - RASDepth: int'(cva6_config_pkg::CVA6ConfigRASDepth), - BTBEntries: int'(cva6_config_pkg::CVA6ConfigBTBEntries), - BHTEntries: int'(cva6_config_pkg::CVA6ConfigBHTEntries), - // idempotent region - NrNonIdempotentRules: unsigned'(2), - NonIdempotentAddrBase: 1024'({64'b0, 64'b0}), - NonIdempotentLength: 1024'({64'b0, 64'b0}), - NrExecuteRegionRules: unsigned'(3), - // DRAM, Boot ROM, Debug Module - ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), - ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), - // cached region - NrCachedRegionRules: unsigned'(1), - CachedRegionAddrBase: 1024'({64'h8000_0000}), - CachedRegionLength: 1024'({64'h40000000}), - // cache config - AxiCompliant: 1'b1, - SwapEndianess: 1'b0, - // debug - DmBaseAddress: 64'h0, - NrPMPEntries: unsigned'(cva6_config_pkg::CVA6ConfigNrPMPEntries) - }; - - // Function being called to check parameters - function automatic void check_cfg (ariane_cfg_t Cfg); - // pragma translate_off - `ifndef VERILATOR - assert(Cfg.RASDepth > 0); - assert(2**$clog2(Cfg.BTBEntries) == Cfg.BTBEntries); - assert(2**$clog2(Cfg.BHTEntries) == Cfg.BHTEntries); - assert(Cfg.NrNonIdempotentRules <= NrMaxRules); - assert(Cfg.NrExecuteRegionRules <= NrMaxRules); - assert(Cfg.NrCachedRegionRules <= NrMaxRules); - assert(Cfg.NrPMPEntries <= 16); - `endif - // pragma translate_on - endfunction - - function automatic logic range_check(logic[63:0] base, logic[63:0] len, logic[63:0] address); - // if len is a power of two, and base is properly aligned, this check could be simplified - // Extend base by one bit to prevent an overflow. - return (address >= base) && (({1'b0, address}) < (65'(base)+len)); - endfunction : range_check - - function automatic logic is_inside_nonidempotent_regions (ariane_cfg_t Cfg, logic[63:0] address); - logic[NrMaxRules-1:0] pass; - pass = '0; - for (int unsigned k = 0; k < Cfg.NrNonIdempotentRules; k++) begin - pass[k] = range_check(Cfg.NonIdempotentAddrBase[k], Cfg.NonIdempotentLength[k], address); - end - return |pass; - endfunction : is_inside_nonidempotent_regions - - function automatic logic is_inside_execute_regions (ariane_cfg_t Cfg, logic[63:0] address); - // if we don't specify any region we assume everything is accessible - logic[NrMaxRules-1:0] pass; - pass = '0; - for (int unsigned k = 0; k < Cfg.NrExecuteRegionRules; k++) begin - pass[k] = range_check(Cfg.ExecuteRegionAddrBase[k], Cfg.ExecuteRegionLength[k], address); - end - return |pass; - endfunction : is_inside_execute_regions - - function automatic logic is_inside_cacheable_regions (ariane_cfg_t Cfg, logic[63:0] address); - automatic logic[NrMaxRules-1:0] pass; - pass = '0; - for (int unsigned k = 0; k < Cfg.NrCachedRegionRules; k++) begin - pass[k] = range_check(Cfg.CachedRegionAddrBase[k], Cfg.CachedRegionLength[k], address); - end - return |pass; - endfunction : is_inside_cacheable_regions - - // TODO: Slowly move those parameters to the new system. - localparam NR_SB_ENTRIES = cva6_config_pkg::CVA6ConfigNrScoreboardEntries; // number of scoreboard entries - localparam TRANS_ID_BITS = $clog2(NR_SB_ENTRIES); // depending on the number of scoreboard entries we need that many bits - // to uniquely identify the entry in the scoreboard - localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; - localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; - localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; - localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; - localparam BITS_SATURATION_COUNTER = 2; - localparam NR_COMMIT_PORTS = cva6_config_pkg::CVA6ConfigNrCommitPorts; - - localparam ENABLE_RENAME = cva6_config_pkg::CVA6ConfigRenameEn; - - localparam ISSUE_WIDTH = 1; - - // depth of store-buffers, this needs to be a power of two - localparam int unsigned DEPTH_SPEC = 4; - - localparam int unsigned DCACHE_TYPE = int'(cva6_config_pkg::CVA6ConfigDcacheType); - // if DCACHE_TYPE = cva6_config_pkg::WT - // we can use a small commit queue since we have a write buffer in the dcache - // we could in principle do without the commit queue in this case, but the timing degrades if we do that due - // to longer paths into the commit stage - // if DCACHE_TYPE = cva6_config_pkg::WB - // allocate more space for the commit buffer to be on the save side, this needs to be a power of two - localparam int unsigned DEPTH_COMMIT = (DCACHE_TYPE == int'(cva6_config_pkg::WT)) ? 4 : 8; - - localparam bit FPGA_EN = cva6_config_pkg::CVA6ConfigFPGAEn; // Is FPGA optimization of CV32A6 - - localparam bit RVC = cva6_config_pkg::CVA6ConfigCExtEn; // Is C extension configuration + // TODO: Slowly move those parameters to the new system. + localparam NR_SB_ENTRIES = cva6_config_pkg::CVA6ConfigNrScoreboardEntries; // number of scoreboard entries + localparam TRANS_ID_BITS = $clog2( + NR_SB_ENTRIES + ); // depending on the number of scoreboard entries we need that many bits + // to uniquely identify the entry in the scoreboard + localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; + localparam BITS_SATURATION_COUNTER = 2; + + localparam ISSUE_WIDTH = 1; + + // depth of store-buffers, this needs to be a power of two + localparam logic [2:0] DEPTH_SPEC = 'd4; + + localparam int unsigned DCACHE_TYPE = int'(cva6_config_pkg::CVA6ConfigDcacheType); + // if DCACHE_TYPE = cva6_config_pkg::WT + // we can use a small commit queue since we have a write buffer in the dcache + // we could in principle do without the commit queue in this case, but the timing degrades if we do that due + // to longer paths into the commit stage + // if DCACHE_TYPE = cva6_config_pkg::WB + // allocate more space for the commit buffer to be on the save side, this needs to be a power of two + localparam logic [2:0] DEPTH_COMMIT = 'd4; + + localparam bit FPGA_EN = cva6_config_pkg::CVA6ConfigFPGAEn; // Is FPGA optimization of CV32A6 + + localparam bit RVC = cva6_config_pkg::CVA6ConfigCExtEn; // Is C extension configuration + + // Transprecision float unit + localparam int unsigned LAT_COMP_FP32 = 'd2; + localparam int unsigned LAT_COMP_FP64 = 'd3; + localparam int unsigned LAT_COMP_FP16 = 'd1; + localparam int unsigned LAT_COMP_FP16ALT = 'd1; + localparam int unsigned LAT_COMP_FP8 = 'd1; + localparam int unsigned LAT_DIVSQRT = 'd2; + localparam int unsigned LAT_NONCOMP = 'd1; + localparam int unsigned LAT_CONV = 'd2; + + localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN - 32{1'b0}}, 32'h0602}; + localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN - 32{1'b0}}, 32'd3}; + + // 32 registers + localparam REG_ADDR_SIZE = 5; + + // Read ports for general purpose register files + localparam NR_RGPR_PORTS = 2; + + // static debug hartinfo + // debug causes + localparam logic [2:0] CauseBreakpoint = 3'h1; + localparam logic [2:0] CauseTrigger = 3'h2; + localparam logic [2:0] CauseRequest = 3'h3; + localparam logic [2:0] CauseSingleStep = 3'h4; + // amount of data count registers implemented + localparam logic [3:0] DataCount = 4'h2; + + // address where data0-15 is shadowed or if shadowed in a CSR + // address of the first CSR used for shadowing the data + localparam logic [11:0] DataAddr = 12'h380; // we are aligned with Rocket here + typedef struct packed { + logic [31:24] zero1; + logic [23:20] nscratch; + logic [19:17] zero0; + logic dataaccess; + logic [15:12] datasize; + logic [11:0] dataaddr; + } hartinfo_t; + + localparam hartinfo_t DebugHartInfo = '{ + zero1: '0, + nscratch: 2, // Debug module needs at least two scratch regs + zero0: '0, + dataaccess: 1'b1, // data registers are memory mapped in the debugger + datasize: DataCount, + dataaddr: DataAddr + }; + + // enables a commit log which matches spikes commit log format for easier trace comparison + localparam bit ENABLE_SPIKE_COMMIT_LOG = 1'b1; + + // ------------- Dangerous ------------- + // if set to zero a flush will not invalidate the cache-lines, in a single core environment + // where coherence is not necessary this can improve performance. This needs to be switched on + // when more than one core is in a system + localparam logic INVALIDATE_ON_FLUSH = 1'b1; -`ifdef PITON_ARIANE - // Floating-point extensions configuration - localparam bit RVF = riscv::IS_XLEN64; // Is F extension enabled - localparam bit RVD = riscv::IS_XLEN64; // Is D extension enabled -`else - // Floating-point extensions configuration - localparam bit RVF = (riscv::IS_XLEN64 | riscv::IS_XLEN32) & riscv::FPU_EN; // Is F extension enabled for both 32 Bit and 64 bit CPU - localparam bit RVD = (riscv::IS_XLEN64 ? 1:0) & riscv::FPU_EN; // Is D extension enabled for only 64 bit CPU -`endif - localparam bit RVA = cva6_config_pkg::CVA6ConfigAExtEn; // Is A extension enabled - localparam bit RVV = cva6_config_pkg::CVA6ConfigVExtEn; - - // Is the accelerator enabled? - localparam bit ENABLE_ACCELERATOR = RVV; // Currently only used by V extension (Ara) - - // Transprecision floating-point extensions configuration - localparam bit XF16 = cva6_config_pkg::CVA6ConfigF16En | RVV; // Is half-precision float extension (Xf16) enabled - localparam bit XF16ALT = cva6_config_pkg::CVA6ConfigF16AltEn; // Is alternative half-precision float extension (Xf16alt) enabled - localparam bit XF8 = cva6_config_pkg::CVA6ConfigF8En; // Is quarter-precision float extension (Xf8) enabled - localparam bit XFVEC = cva6_config_pkg::CVA6ConfigFVecEn; // Is vectorial float extension (Xfvec) enabled - - // Transprecision float unit - localparam int unsigned LAT_COMP_FP32 = 'd2; - localparam int unsigned LAT_COMP_FP64 = 'd3; - localparam int unsigned LAT_COMP_FP16 = 'd1; - localparam int unsigned LAT_COMP_FP16ALT = 'd1; - localparam int unsigned LAT_COMP_FP8 = 'd1; - localparam int unsigned LAT_DIVSQRT = 'd2; - localparam int unsigned LAT_NONCOMP = 'd1; - localparam int unsigned LAT_CONV = 'd2; - - // -------------------------------------- - // vvvv Don't change these by hand! vvvv - localparam bit FP_PRESENT = RVF | RVD | XF16 | XF16ALT | XF8; - - // Length of widest floating-point format - localparam FLEN = RVD ? 64 : // D ext. - RVF ? 32 : // F ext. - XF16 ? 16 : // Xf16 ext. - XF16ALT ? 16 : // Xf16alt ext. - XF8 ? 8 : // Xf8 ext. - 1; // Unused in case of no FP - - localparam bit NSX = XF16 | XF16ALT | XF8 | XFVEC; // Are non-standard extensions present? - - localparam bit RVFVEC = RVF & XFVEC & FLEN>32; // FP32 vectors available if vectors and larger fmt enabled - localparam bit XF16VEC = XF16 & XFVEC & FLEN>16; // FP16 vectors available if vectors and larger fmt enabled - localparam bit XF16ALTVEC = XF16ALT & XFVEC & FLEN>16; // FP16ALT vectors available if vectors and larger fmt enabled - localparam bit XF8VEC = XF8 & XFVEC & FLEN>8; // FP8 vectors available if vectors and larger fmt enabled - // ^^^^ until here ^^^^ - // --------------------- - - localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN-32{1'b0}}, 32'h0602}; - localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN-32{1'b0}}, 32'd3}; - - localparam riscv::xlen_t ISA_CODE = (riscv::XLEN'(RVA) << 0) // A - Atomic Instructions extension - | (riscv::XLEN'(RVC) << 2) // C - Compressed extension - | (riscv::XLEN'(RVD) << 3) // D - Double precsision floating-point extension - | (riscv::XLEN'(RVF) << 5) // F - Single precsision floating-point extension - | (riscv::XLEN'(1 ) << 8) // I - RV32I/64I/128I base ISA - | (riscv::XLEN'(1 ) << 12) // M - Integer Multiply/Divide extension - | (riscv::XLEN'(0 ) << 13) // N - User level interrupts supported - | (riscv::XLEN'(1 ) << 18) // S - Supervisor mode implemented - | (riscv::XLEN'(1 ) << 20) // U - User mode implemented - | (riscv::XLEN'(RVV) << 21) // V - Vector extension - | (riscv::XLEN'(NSX) << 23) // X - Non-standard extensions present - | ((riscv::XLEN == 64 ? 2 : 1) << riscv::XLEN-2); // MXL - - // 32 registers + 1 bit for re-naming = 6 - localparam REG_ADDR_SIZE = 6; - - localparam bit CVXIF_PRESENT = cva6_config_pkg::CVA6ConfigCvxifEn; - - // when cvx interface or the accelerator port is present, use an additional writeback port - localparam NR_WB_PORTS = (CVXIF_PRESENT || ENABLE_ACCELERATOR) ? 5 : 4; - - // Read ports for general purpose register files - localparam NR_RGPR_PORTS = 2; - typedef logic [(NR_RGPR_PORTS == 3 ? riscv::XLEN : FLEN)-1:0] rs3_len_t; - - // static debug hartinfo - localparam ariane_dm_pkg::hartinfo_t DebugHartInfo = '{ - zero1: '0, - nscratch: 2, // Debug module needs at least two scratch regs - zero0: '0, - dataaccess: 1'b1, // data registers are memory mapped in the debugger - datasize: ariane_dm_pkg::DataCount, - dataaddr: ariane_dm_pkg::DataAddr - }; - - // enables a commit log which matches spikes commit log format for easier trace comparison - localparam bit ENABLE_SPIKE_COMMIT_LOG = 1'b1; - - // ------------- Dangerouse ------------- - // if set to zero a flush will not invalidate the cache-lines, in a single core environment - // where coherence is not necessary this can improve performance. This needs to be switched on - // when more than one core is in a system - localparam logic INVALIDATE_ON_FLUSH = 1'b1; `ifdef SPIKE_TANDEM - // enable performance cycle counter, if set to zero mcycle will be incremented - // with instret (non RISC-V conformal) - localparam bit ENABLE_CYCLE_COUNT = 1'b0; - // mark WIF as nop - localparam bit ENABLE_WFI = 1'b0; - // Spike zeros tval on all exception except memory faults - localparam bit ZERO_TVAL = 1'b1; + // Spike still places 0 in TVAL for ENV_CALL_* exceptions. + // This may eventually go away when Spike starts to handle TVAL for *all* exceptions. + localparam bit ZERO_TVAL = 1'b1; `else - localparam bit ENABLE_CYCLE_COUNT = 1'b1; - localparam bit ENABLE_WFI = 1'b1; - localparam bit ZERO_TVAL = 1'b0; + localparam bit ZERO_TVAL = 1'b0; `endif - // read mask for SSTATUS over MMSTATUS - localparam logic [63:0] SMODE_STATUS_READ_MASK = riscv::SSTATUS_UIE - | riscv::SSTATUS_SIE - | riscv::SSTATUS_SPIE - | riscv::SSTATUS_SPP - | riscv::SSTATUS_FS - | riscv::SSTATUS_XS - | riscv::SSTATUS_SUM - | riscv::SSTATUS_MXR - | riscv::SSTATUS_UPIE - | riscv::SSTATUS_SPIE - | riscv::SSTATUS_UXL - | riscv::SSTATUS_SD; - - localparam logic [63:0] SMODE_STATUS_WRITE_MASK = riscv::SSTATUS_SIE + // read mask for SSTATUS over MMSTATUS + localparam logic [63:0] SMODE_STATUS_READ_MASK = riscv::SSTATUS_UIE + | riscv::SSTATUS_SIE | riscv::SSTATUS_SPIE | riscv::SSTATUS_SPP | riscv::SSTATUS_FS + | riscv::SSTATUS_XS | riscv::SSTATUS_SUM - | riscv::SSTATUS_MXR; - // --------------- - // AXI - // --------------- - - localparam AXI_ID_WIDTH = cva6_config_pkg::CVA6ConfigAxiIdWidth; - localparam AXI_ADDR_WIDTH = cva6_config_pkg::CVA6ConfigAxiAddrWidth; - localparam AXI_DATA_WIDTH = cva6_config_pkg::CVA6ConfigAxiDataWidth; - localparam FETCH_USER_WIDTH = cva6_config_pkg::CVA6ConfigFetchUserWidth; - localparam DATA_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; - localparam AXI_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn | cva6_config_pkg::CVA6ConfigFetchUserEn; - localparam AXI_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; - localparam DATA_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn; - localparam FETCH_USER_EN = cva6_config_pkg::CVA6ConfigFetchUserEn; - - // --------------- - // Fetch Stage - // --------------- - - // leave as is (fails with >8 entries and wider fetch width) - localparam int unsigned FETCH_FIFO_DEPTH = 4; - localparam int unsigned FETCH_WIDTH = 32; - // maximum instructions we can fetch on one request (we support compressed instructions) - localparam int unsigned INSTR_PER_FETCH = RVC == 1'b1 ? (FETCH_WIDTH / 16) : 1; - localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2(ariane_pkg::INSTR_PER_FETCH) : 1; - - // --------------- - // Enable BITMANIP - // --------------- - localparam bit BITMANIP = cva6_config_pkg::CVA6ConfigBExtEn; - - // Only use struct when signals have same direction - // exception - typedef struct packed { - riscv::xlen_t cause; // cause of exception - riscv::xlen_t tval; // additional information of causing exception (e.g.: instruction causing it), - // address of LD/ST fault - logic valid; - } exception_t; - - typedef enum logic [2:0] { - NoCF, // No control flow prediction - Branch, // Branch - Jump, // Jump to address from immediate - JumpR, // Jump to address from registers - Return // Return Address Prediction - } cf_t; - - // branch-predict - // this is the struct we get back from ex stage and we will use it to update - // all the necessary data structures - // bp_resolve_t - typedef struct packed { - logic valid; // prediction with all its values is valid - logic [riscv::VLEN-1:0] pc; // PC of predict or mis-predict - logic [riscv::VLEN-1:0] target_address; // target address at which to jump, or not - logic is_mispredict; // set if this was a mis-predict - logic is_taken; // branch is taken - cf_t cf_type; // Type of control flow change - } bp_resolve_t; - - // branchpredict scoreboard entry - // this is the struct which we will inject into the pipeline to guide the various - // units towards the correct branch decision and resolve - typedef struct packed { - cf_t cf; // type of control flow prediction - logic [riscv::VLEN-1:0] predict_address; // target address at which to jump, or not - } branchpredict_sbe_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] pc; // update at PC - logic [riscv::VLEN-1:0] target_address; - } btb_update_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] target_address; - } btb_prediction_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] ra; - } ras_t; - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] pc; // update at PC - logic taken; - } bht_update_t; - - typedef struct packed { - logic valid; - logic taken; - } bht_prediction_t; - - typedef struct packed { - logic valid; - logic [1:0] saturation_counter; - } bht_t; - - typedef enum logic[3:0] { - NONE, // 0 - LOAD, // 1 - STORE, // 2 - ALU, // 3 - CTRL_FLOW, // 4 - MULT, // 5 - CSR, // 6 - FPU, // 7 - FPU_VEC, // 8 - CVXIF, // 9 - ACCEL // 10 - } fu_t; - - localparam EXC_OFF_RST = 8'h80; - - localparam SupervisorIrq = 1; - localparam MachineIrq = 0; - - // All information needed to determine whether we need to associate an interrupt - // with the corresponding instruction or not. - typedef struct packed { - riscv::xlen_t mie; - riscv::xlen_t mip; - riscv::xlen_t mideleg; - logic sie; - logic global_enable; - } irq_ctrl_t; - - // --------------- - // Cache config - // --------------- - -// for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h + | riscv::SSTATUS_MXR + | riscv::SSTATUS_UPIE + | riscv::SSTATUS_SPIE + | riscv::SSTATUS_UXL + | riscv::SSTATUS_SD; + + localparam logic [63:0] SMODE_STATUS_WRITE_MASK = riscv::SSTATUS_SIE + | riscv::SSTATUS_SPIE + | riscv::SSTATUS_SPP + | riscv::SSTATUS_FS + | riscv::SSTATUS_SUM + | riscv::SSTATUS_MXR; + // --------------- + // AXI + // --------------- + + localparam FETCH_USER_WIDTH = cva6_config_pkg::CVA6ConfigFetchUserWidth; + localparam DATA_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; + localparam AXI_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn | cva6_config_pkg::CVA6ConfigFetchUserEn; + localparam AXI_USER_WIDTH = cva6_config_pkg::CVA6ConfigDataUserWidth; + localparam DATA_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn; + localparam FETCH_USER_EN = cva6_config_pkg::CVA6ConfigFetchUserEn; + + typedef enum logic { + SINGLE_REQ, + CACHE_LINE_REQ + } ad_req_t; + + // --------------- + // Fetch Stage + // --------------- + + // leave as is (fails with >8 entries and wider fetch width) + localparam int unsigned FETCH_FIFO_DEPTH = 4; + localparam int unsigned FETCH_WIDTH = 32; + // maximum instructions we can fetch on one request (we support compressed instructions) + localparam int unsigned INSTR_PER_FETCH = RVC == 1'b1 ? (FETCH_WIDTH / 16) : 1; + localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2( + ariane_pkg::INSTR_PER_FETCH + ) : 1; + + // --------------- + // Enable BITMANIP + // --------------- + localparam bit BITMANIP = cva6_config_pkg::CVA6ConfigBExtEn; + + // Only use struct when signals have same direction + // exception + typedef struct packed { + riscv::xlen_t cause; // cause of exception + riscv::xlen_t tval; // additional information of causing exception (e.g.: instruction causing it), + // address of LD/ST fault + logic valid; + } exception_t; + + typedef enum logic [2:0] { + NoCF, // No control flow prediction + Branch, // Branch + Jump, // Jump to address from immediate + JumpR, // Jump to address from registers + Return // Return Address Prediction + } cf_t; + + // branch-predict + // this is the struct we get back from ex stage and we will use it to update + // all the necessary data structures + // bp_resolve_t + typedef struct packed { + logic valid; // prediction with all its values is valid + logic [riscv::VLEN-1:0] pc; // PC of predict or mis-predict + logic [riscv::VLEN-1:0] target_address; // target address at which to jump, or not + logic is_mispredict; // set if this was a mis-predict + logic is_taken; // branch is taken + cf_t cf_type; // Type of control flow change + } bp_resolve_t; + + // branchpredict scoreboard entry + // this is the struct which we will inject into the pipeline to guide the various + // units towards the correct branch decision and resolve + typedef struct packed { + cf_t cf; // type of control flow prediction + logic [riscv::VLEN-1:0] predict_address; // target address at which to jump, or not + } branchpredict_sbe_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] pc; // update at PC + logic [riscv::VLEN-1:0] target_address; + } btb_update_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] target_address; + } btb_prediction_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] ra; + } ras_t; + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] pc; // update at PC + logic taken; + } bht_update_t; + + typedef struct packed { + logic valid; + logic taken; + } bht_prediction_t; + + typedef struct packed { + logic valid; + logic [1:0] saturation_counter; + } bht_t; + + typedef enum logic [3:0] { + NONE, // 0 + LOAD, // 1 + STORE, // 2 + ALU, // 3 + CTRL_FLOW, // 4 + MULT, // 5 + CSR, // 6 + FPU, // 7 + FPU_VEC, // 8 + CVXIF, // 9 + ACCEL // 10 + } fu_t; + + localparam EXC_OFF_RST = 8'h80; + + localparam SupervisorIrq = 1; + localparam MachineIrq = 0; + + // All information needed to determine whether we need to associate an interrupt + // with the corresponding instruction or not. + typedef struct packed { + riscv::xlen_t mie; + riscv::xlen_t mip; + riscv::xlen_t mideleg; + logic sie; + logic global_enable; + } irq_ctrl_t; + + // --------------- + // Cache config + // --------------- + + // for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h `ifdef PITON_ARIANE `ifndef CONFIG_L1I_CACHELINE_WIDTH - `define CONFIG_L1I_CACHELINE_WIDTH 128 + `define CONFIG_L1I_CACHELINE_WIDTH 128 `endif `ifndef CONFIG_L1I_ASSOCIATIVITY - `define CONFIG_L1I_ASSOCIATIVITY 4 + `define CONFIG_L1I_ASSOCIATIVITY 4 `endif `ifndef CONFIG_L1I_SIZE - `define CONFIG_L1I_SIZE 16*1024 + `define CONFIG_L1I_SIZE 16*1024 `endif `ifndef CONFIG_L1D_CACHELINE_WIDTH - `define CONFIG_L1D_CACHELINE_WIDTH 128 + `define CONFIG_L1D_CACHELINE_WIDTH 128 `endif `ifndef CONFIG_L1D_ASSOCIATIVITY - `define CONFIG_L1D_ASSOCIATIVITY 8 + `define CONFIG_L1D_ASSOCIATIVITY 8 `endif `ifndef CONFIG_L1D_SIZE - `define CONFIG_L1D_SIZE 32*1024 + `define CONFIG_L1D_SIZE 32*1024 `endif `ifndef L15_THREADID_WIDTH - `define L15_THREADID_WIDTH 3 + `define L15_THREADID_WIDTH 3 `endif - // I$ - localparam int unsigned ICACHE_LINE_WIDTH = `CONFIG_L1I_CACHELINE_WIDTH; - localparam int unsigned ICACHE_SET_ASSOC = `CONFIG_L1I_ASSOCIATIVITY; - localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(`CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); - localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; - localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit - // D$ - localparam int unsigned DCACHE_LINE_WIDTH = `CONFIG_L1D_CACHELINE_WIDTH; - localparam int unsigned DCACHE_SET_ASSOC = `CONFIG_L1D_ASSOCIATIVITY; - localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(`CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); - localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; - localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit - localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; - - localparam int unsigned MEM_TID_WIDTH = `L15_THREADID_WIDTH; + // I$ + localparam int unsigned ICACHE_LINE_WIDTH = `CONFIG_L1I_CACHELINE_WIDTH; + localparam int unsigned ICACHE_SET_ASSOC = `CONFIG_L1I_ASSOCIATIVITY; + localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(`CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); + localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; + localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit + // D$ + localparam int unsigned DCACHE_LINE_WIDTH = `CONFIG_L1D_CACHELINE_WIDTH; + localparam int unsigned DCACHE_SET_ASSOC = `CONFIG_L1D_ASSOCIATIVITY; + localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(`CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); + localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; + localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : 128; // in bit + localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; + + localparam int unsigned MEM_TID_WIDTH = `L15_THREADID_WIDTH; `else - // I$ - localparam int unsigned CONFIG_L1I_SIZE = cva6_config_pkg::CVA6ConfigIcacheByteSize; // in byte - localparam int unsigned ICACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigIcacheSetAssoc; // number of ways - localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); // in bit, contains also offset width - localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN-ICACHE_INDEX_WIDTH; // in bit - localparam int unsigned ICACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit - localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit - // D$ - localparam int unsigned CONFIG_L1D_SIZE = cva6_config_pkg::CVA6ConfigDcacheByteSize; // in byte - localparam int unsigned DCACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigDcacheSetAssoc; // number of ways - localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); // in bit, contains also offset width - localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN-DCACHE_INDEX_WIDTH; // in bit - localparam int unsigned DCACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit - localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit - localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; - - localparam int unsigned MEM_TID_WIDTH = cva6_config_pkg::CVA6ConfigMemTidWidth; + // I$ + localparam int unsigned CONFIG_L1I_SIZE = cva6_config_pkg::CVA6ConfigIcacheByteSize; // in byte + localparam int unsigned ICACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigIcacheSetAssoc; // number of ways + localparam int unsigned ICACHE_INDEX_WIDTH = $clog2( + CONFIG_L1I_SIZE / ICACHE_SET_ASSOC + ); // in bit, contains also offset width + localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; // in bit + localparam int unsigned ICACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit + localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit + // D$ + localparam int unsigned CONFIG_L1D_SIZE = cva6_config_pkg::CVA6ConfigDcacheByteSize; // in byte + localparam int unsigned DCACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigDcacheSetAssoc; // number of ways + localparam int unsigned DCACHE_INDEX_WIDTH = $clog2( + CONFIG_L1D_SIZE / DCACHE_SET_ASSOC + ); // in bit, contains also offset width + localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; // in bit + localparam int unsigned DCACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit + localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit + localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH; + + localparam int unsigned MEM_TID_WIDTH = cva6_config_pkg::CVA6ConfigMemTidWidth; `endif - localparam int unsigned DCACHE_TID_WIDTH = cva6_config_pkg::CVA6ConfigDcacheIdWidth; - - localparam int unsigned WT_DCACHE_WBUF_DEPTH = cva6_config_pkg::CVA6ConfigWtDcacheWbufDepth; - - // --------------- - // EX Stage - // --------------- - - typedef enum logic [7:0] { // basic ALU op - ADD, SUB, ADDW, SUBW, - // logic operations - XORL, ORL, ANDL, - // shifts - SRA, SRL, SLL, SRLW, SLLW, SRAW, - // comparisons - LTS, LTU, GES, GEU, EQ, NE, - // jumps - JALR, BRANCH, - // set lower than operations - SLTS, SLTU, - // CSR functions - MRET, SRET, DRET, ECALL, WFI, FENCE, FENCE_I, SFENCE_VMA, CSR_WRITE, CSR_READ, CSR_SET, CSR_CLEAR, - // LSU functions - LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU, - // Atomic Memory Operations - AMO_LRW, AMO_LRD, AMO_SCW, AMO_SCD, - AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW, AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW, AMO_MINWU, - AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD, AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND, AMO_MINDU, - // Multiplications - MUL, MULH, MULHU, MULHSU, MULW, - // Divisions - DIV, DIVU, DIVW, DIVUW, REM, REMU, REMW, REMUW, - // Floating-Point Load and Store Instructions - FLD, FLW, FLH, FLB, FSD, FSW, FSH, FSB, - // Floating-Point Computational Instructions - FADD, FSUB, FMUL, FDIV, FMIN_MAX, FSQRT, FMADD, FMSUB, FNMSUB, FNMADD, - // Floating-Point Conversion and Move Instructions - FCVT_F2I, FCVT_I2F, FCVT_F2F, FSGNJ, FMV_F2X, FMV_X2F, - // Floating-Point Compare Instructions - FCMP, - // Floating-Point Classify Instruction - FCLASS, - // Vectorial Floating-Point Instructions that don't directly map onto the scalar ones - VFMIN, VFMAX, VFSGNJ, VFSGNJN, VFSGNJX, VFEQ, VFNE, VFLT, VFGE, VFLE, VFGT, VFCPKAB_S, VFCPKCD_S, VFCPKAB_D, VFCPKCD_D, - // Offload Instructions to be directed into cv_x_if - OFFLOAD, - // Or-Combine and REV8 - ORCB, REV8, - // Bitwise Rotation - ROL, ROLW, ROR, RORI, RORIW, RORW, - // Sign and Zero Extend - SEXTB, SEXTH, ZEXTH, - // Count population - CPOP, CPOPW, - // Count Leading/Training Zeros - CLZ, CLZW, CTZ, CTZW, - // Carry less multiplication Op's - CLMUL, CLMULH, CLMULR, - // Single bit instructions Op's - BCLR, BCLRI, BEXT, BEXTI, BINV, BINVI, BSET, BSETI, - // Integer minimum/maximum - MAX, MAXU, MIN, MINU, - // Shift with Add Unsigned Word and Unsigned Word Op's (Bitmanip) - SH1ADDUW, SH2ADDUW, SH3ADDUW, ADDUW, SLLIUW, - // Shift with Add (Bitmanip) - SH1ADD, SH2ADD, SH3ADD, - // Bitmanip Logical with negate op (Bitmanip) - ANDN, ORN, XNOR, - // Accelerator operations - ACCEL_OP, ACCEL_OP_FS1, ACCEL_OP_FD, ACCEL_OP_LOAD, ACCEL_OP_STORE - } fu_op; - - typedef struct packed { - fu_t fu; - fu_op operation; - riscv::xlen_t operand_a; - riscv::xlen_t operand_b; - riscv::xlen_t imm; - logic [TRANS_ID_BITS-1:0] trans_id; - } fu_data_t; - - function automatic logic op_is_branch (input fu_op op); - unique case (op) inside - EQ, NE, LTS, GES, LTU, GEU: return 1'b1; - default : return 1'b0; // all other ops - endcase - endfunction - - // ------------------------------- - // Extract Src/Dst FP Reg from Op - // ------------------------------- - function automatic logic is_rs1_fpr (input fu_op op); - if (FP_PRESENT) begin // makes function static for non-fp case - unique case (op) inside - [FMUL:FNMADD], // Computational Operations (except ADD/SUB) - FCVT_F2I, // Float-Int Casts - FCVT_F2F, // Float-Float Casts - FSGNJ, // Sign Injections - FMV_F2X, // FPR-GPR Moves - FCMP, // Comparisons - FCLASS, // Classifications - [VFMIN:VFCPKCD_D], // Additional Vectorial FP ops - ACCEL_OP_FS1 : return 1'b1; // Accelerator instructions - default : return 1'b0; // all other ops - endcase - end else - return 1'b0; - endfunction - - function automatic logic is_rs2_fpr (input fu_op op); - if (FP_PRESENT) begin // makes function static for non-fp case - unique case (op) inside - [FSD:FSB], // FP Stores - [FADD:FMIN_MAX], // Computational Operations (no sqrt) - [FMADD:FNMADD], // Fused Computational Operations - FCVT_F2F, // Vectorial F2F Conversions requrie target - [FSGNJ:FMV_F2X], // Sign Injections and moves mapped to SGNJ - FCMP, // Comparisons - [VFMIN:VFCPKCD_D] : return 1'b1; // Additional Vectorial FP ops - default : return 1'b0; // all other ops - endcase - end else - return 1'b0; - endfunction - - // ternary operations encode the rs3 address in the imm field, also add/sub - function automatic logic is_imm_fpr (input fu_op op); - if (FP_PRESENT) begin // makes function static for non-fp case - unique case (op) inside - [FADD:FSUB], // ADD/SUB need inputs as Operand B/C - [FMADD:FNMADD], // Fused Computational Operations - [VFCPKAB_S:VFCPKCD_D] : return 1'b1; // Vectorial FP cast and pack ops - default : return 1'b0; // all other ops - endcase - end else - return 1'b0; - endfunction - - function automatic logic is_rd_fpr (input fu_op op); - if (FP_PRESENT) begin // makes function static for non-fp case - unique case (op) inside - [FLD:FLB], // FP Loads - [FADD:FNMADD], // Computational Operations - FCVT_I2F, // Int-Float Casts - FCVT_F2F, // Float-Float Casts - FSGNJ, // Sign Injections - FMV_X2F, // GPR-FPR Moves - [VFMIN:VFSGNJX], // Vectorial MIN/MAX and SGNJ - [VFCPKAB_S:VFCPKCD_D], // Vectorial FP cast and pack ops - ACCEL_OP_FD : return 1'b1; // Accelerator instructions - default : return 1'b0; // all other ops - endcase - end else - return 1'b0; - endfunction - - function automatic logic is_amo (fu_op op); - case (op) inside - [AMO_LRW:AMO_MINDU]: begin - return 1'b1; - end - default: return 1'b0; - endcase - endfunction - - typedef struct packed { - logic valid; - logic [riscv::VLEN-1:0] vaddr; - logic overflow; - riscv::xlen_t data; - logic [(riscv::XLEN/8)-1:0] be; - fu_t fu; - fu_op operation; - logic [TRANS_ID_BITS-1:0] trans_id; - } lsu_ctrl_t; - - // --------------- - // IF/ID Stage - // --------------- - // store the decompressed instruction - typedef struct packed { - logic [riscv::VLEN-1:0] address; // the address of the instructions from below - logic [31:0] instruction; // instruction word - branchpredict_sbe_t branch_predict; // this field contains branch prediction information regarding the forward branch path - exception_t ex; // this field contains exceptions which might have happened earlier, e.g.: fetch exceptions - } fetch_entry_t; - - // --------------- - // ID/EX/WB Stage - // --------------- - - localparam RVFI = cva6_config_pkg::CVA6ConfigRvfiTrace; - typedef rvfi_pkg::rvfi_instr_t [NR_COMMIT_PORTS-1:0] rvfi_port_t; - - typedef struct packed { - logic [riscv::VLEN-1:0] pc; // PC of instruction - logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry - // with the transaction id in any case make the width more generic - fu_t fu; // functional unit to use - fu_op op; // operation to perform in each functional unit - logic [REG_ADDR_SIZE-1:0] rs1; // register source address 1 - logic [REG_ADDR_SIZE-1:0] rs2; // register source address 2 - logic [REG_ADDR_SIZE-1:0] rd; // register destination address - riscv::xlen_t result; // for unfinished instructions this field also holds the immediate, - // for unfinished floating-point that are partly encoded in rs2, this field also holds rs2 - // for unfinished floating-point fused operations (FMADD, FMSUB, FNMADD, FNMSUB) - // this field holds the address of the third operand from the floating-point register file - logic valid; // is the result valid - logic use_imm; // should we use the immediate as operand b? - logic use_zimm; // use zimm as operand a - logic use_pc; // set if we need to use the PC as operand a, PC from exception - exception_t ex; // exception has occurred - branchpredict_sbe_t bp; // branch predict scoreboard data structure - logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if - // we want jump accordingly e.g.: +4, +2 - riscv::xlen_t rs1_rdata; // information needed by RVFI - riscv::xlen_t rs2_rdata; // information needed by RVFI - logic [riscv::VLEN-1:0] lsu_addr; // information needed by RVFI - logic [(riscv::XLEN/8)-1:0] lsu_rmask; // information needed by RVFI - logic [(riscv::XLEN/8)-1:0] lsu_wmask; // information needed by RVFI - riscv::xlen_t lsu_wdata; // information needed by RVFI - logic vfp; // is this a vector floating-point instruction? - } scoreboard_entry_t; - - // --------------- - // MMU instanciation - // --------------- - localparam bit MMU_PRESENT = cva6_config_pkg::CVA6ConfigMmuPresent; - - localparam int unsigned INSTR_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigInstrTlbEntries; - localparam int unsigned DATA_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigDataTlbEntries; - - // ------------------- - // Performance counter - // ------------------- - localparam bit PERF_COUNTER_EN = cva6_config_pkg::CVA6ConfigPerfCounterEn; - localparam int unsigned MHPMCounterNum = 6; - - // -------------------- - // Atomics - // -------------------- - typedef enum logic [3:0] { - AMO_NONE =4'b0000, - AMO_LR =4'b0001, - AMO_SC =4'b0010, - AMO_SWAP =4'b0011, - AMO_ADD =4'b0100, - AMO_AND =4'b0101, - AMO_OR =4'b0110, - AMO_XOR =4'b0111, - AMO_MAX =4'b1000, - AMO_MAXU =4'b1001, - AMO_MIN =4'b1010, - AMO_MINU =4'b1011, - AMO_CAS1 =4'b1100, // unused, not part of riscv spec, but provided in OpenPiton - AMO_CAS2 =4'b1101 // unused, not part of riscv spec, but provided in OpenPiton - } amo_t; - - typedef struct packed { - logic valid; // valid flag - logic is_2M; // - logic is_1G; // - logic [27-1:0] vpn; // VPN (39bits) = 27bits + 12bits offset - logic [ASID_WIDTH-1:0] asid; - riscv::pte_t content; - } tlb_update_t; - - // Bits required for representation of physical address space as 4K pages - // (e.g. 27*4K == 39bit address space). - localparam PPN4K_WIDTH = 38; - - typedef struct packed { - logic valid; // valid flag - logic is_4M; // - logic [20-1:0] vpn; //VPN (32bits) = 20bits + 12bits offset - logic [9-1:0] asid; //ASID length = 9 for Sv32 mmu - riscv::pte_sv32_t content; - } tlb_update_sv32_t; - - typedef struct packed { - logic valid; // valid flag - logic is_2M; // - logic is_4M; // - logic is_1G; // - logic [VPN_LEN-1:0] vpn; // - logic [ASID_LEN-1:0] asid; // - riscv::pte_cva6_t content; - } tlb_update_cva6_t; - - typedef enum logic [1:0] { - FE_NONE, - FE_INSTR_ACCESS_FAULT, - FE_INSTR_PAGE_FAULT - } frontend_exception_t; - - // ---------------------- - // cache request ports - // ---------------------- - // I$ address translation requests - typedef struct packed { - logic fetch_valid; // address translation valid - logic [riscv::PLEN-1:0] fetch_paddr; // physical address in - exception_t fetch_exception; // exception occurred during fetch - } icache_areq_i_t; - - typedef struct packed { - logic fetch_req; // address translation request - logic [riscv::VLEN-1:0] fetch_vaddr; // virtual address out - } icache_areq_o_t; - - // I$ data requests - typedef struct packed { - logic req; // we request a new word - logic kill_s1; // kill the current request - logic kill_s2; // kill the last request - logic spec; // request is speculative - logic [riscv::VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup - } icache_dreq_i_t; - - typedef struct packed { - logic ready; // icache is ready - logic valid; // signals a valid read - logic [FETCH_WIDTH-1:0] data; // 2+ cycle out: tag - logic [FETCH_USER_WIDTH-1:0] user; // User bits - logic [riscv::VLEN-1:0] vaddr; // virtual address out - exception_t ex; // we've encountered an exception - } icache_dreq_o_t; - - // AMO request going to cache. this request is unconditionally valid as soon - // as request goes high. - // Furthermore, those signals are kept stable until the response indicates - // completion by asserting ack. - typedef struct packed { - logic req; // this request is valid - amo_t amo_op; // atomic memory operation to perform - logic [1:0] size; // 2'b10 --> word operation, 2'b11 --> double word operation - logic [63:0] operand_a; // address - logic [63:0] operand_b; // data as layouted in the register - } amo_req_t; - - // AMO response coming from cache. - typedef struct packed { - logic ack; // response is valid - logic [63:0] result; // sign-extended, result - } amo_resp_t; - - // D$ data requests - typedef struct packed { - logic [DCACHE_INDEX_WIDTH-1:0] address_index; - logic [DCACHE_TAG_WIDTH-1:0] address_tag; - riscv::xlen_t data_wdata; - logic [DCACHE_USER_WIDTH-1:0] data_wuser; - logic data_req; - logic data_we; - logic [(riscv::XLEN/8)-1:0] data_be; - logic [1:0] data_size; - logic [DCACHE_TID_WIDTH-1:0] data_id; - logic kill_req; - logic tag_valid; - } dcache_req_i_t; - - typedef struct packed { - logic data_gnt; - logic data_rvalid; - logic [DCACHE_TID_WIDTH-1:0] data_rid; - riscv::xlen_t data_rdata; - logic [DCACHE_USER_WIDTH-1:0] data_ruser; - } dcache_req_o_t; - - // ---------------------- - // Arithmetic Functions - // ---------------------- - function automatic riscv::xlen_t sext32 (logic [31:0] operand); - return {{riscv::XLEN-32{operand[31]}}, operand[31:0]}; - endfunction - - // ---------------------- - // Immediate functions - // ---------------------- - function automatic logic [riscv::VLEN-1:0] uj_imm (logic [31:0] instruction_i); - return { {44+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[19:12], instruction_i[20], instruction_i[30:21], 1'b0 }; - endfunction - - function automatic logic [riscv::VLEN-1:0] i_imm (logic [31:0] instruction_i); - return { {52+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31:20] }; - endfunction - - function automatic logic [riscv::VLEN-1:0] sb_imm (logic [31:0] instruction_i); - return { {51+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31], instruction_i[7], instruction_i[30:25], instruction_i[11:8], 1'b0 }; - endfunction - - // ---------------------- - // LSU Functions - // ---------------------- - // align data to address e.g.: shift data to be naturally 64 - function automatic riscv::xlen_t data_align (logic [2:0] addr, logic [63:0] data); - // Set addr[2] to 1'b0 when 32bits - logic [2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]}; - logic [63:0] data_tmp = {64{1'b0}}; - case (addr_tmp) - 3'b000: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-1:0]}; - 3'b001: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]}; - 3'b010: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]}; - 3'b011: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]}; - 3'b100: data_tmp = {data[31:0], data[63:32]}; - 3'b101: data_tmp = {data[23:0], data[63:24]}; - 3'b110: data_tmp = {data[15:0], data[63:16]}; - 3'b111: data_tmp = {data[7:0], data[63:8]}; - endcase - return data_tmp[riscv::XLEN-1:0]; - endfunction - - // generate byte enable mask - function automatic logic [7:0] be_gen(logic [2:0] addr, logic [1:0] size); - case (size) - 2'b11: begin - return 8'b1111_1111; - end - 2'b10: begin - case (addr[2:0]) - 3'b000: return 8'b0000_1111; - 3'b001: return 8'b0001_1110; - 3'b010: return 8'b0011_1100; - 3'b011: return 8'b0111_1000; - 3'b100: return 8'b1111_0000; - default: ; // Do nothing - endcase - end - 2'b01: begin - case (addr[2:0]) - 3'b000: return 8'b0000_0011; - 3'b001: return 8'b0000_0110; - 3'b010: return 8'b0000_1100; - 3'b011: return 8'b0001_1000; - 3'b100: return 8'b0011_0000; - 3'b101: return 8'b0110_0000; - 3'b110: return 8'b1100_0000; - default: ; // Do nothing - endcase - end - 2'b00: begin - case (addr[2:0]) - 3'b000: return 8'b0000_0001; - 3'b001: return 8'b0000_0010; - 3'b010: return 8'b0000_0100; - 3'b011: return 8'b0000_1000; - 3'b100: return 8'b0001_0000; - 3'b101: return 8'b0010_0000; - 3'b110: return 8'b0100_0000; - 3'b111: return 8'b1000_0000; - endcase - end - endcase - return 8'b0; - endfunction - - function automatic logic [3:0] be_gen_32(logic [1:0] addr, logic [1:0] size); - case (size) - 2'b10: begin - return 4'b1111; - end - 2'b01: begin - case (addr[1:0]) - 2'b00: return 4'b0011; - 2'b01: return 4'b0110; - 2'b10: return 4'b1100; - default: ; // Do nothing - endcase - end - 2'b00: begin - case (addr[1:0]) - 2'b00: return 4'b0001; - 2'b01: return 4'b0010; - 2'b10: return 4'b0100; - 2'b11: return 4'b1000; - endcase - end - default: return 4'b0; - endcase - return 4'b0; - endfunction - - // ---------------------- - // Extract Bytes from Op - // ---------------------- - function automatic logic [1:0] extract_transfer_size(fu_op op); - case (op) - LD, SD, FLD, FSD, - AMO_LRD, AMO_SCD, - AMO_SWAPD, AMO_ADDD, - AMO_ANDD, AMO_ORD, - AMO_XORD, AMO_MAXD, - AMO_MAXDU, AMO_MIND, - AMO_MINDU: begin - return 2'b11; - end - LW, LWU, SW, FLW, FSW, - AMO_LRW, AMO_SCW, - AMO_SWAPW, AMO_ADDW, - AMO_ANDW, AMO_ORW, - AMO_XORW, AMO_MAXW, - AMO_MAXWU, AMO_MINW, - AMO_MINWU: begin - return 2'b10; - end - LH, LHU, SH, FLH, FSH: return 2'b01; - LB, LBU, SB, FLB, FSB: return 2'b00; - default: return 2'b11; - endcase - endfunction + localparam int unsigned DCACHE_TID_WIDTH = cva6_config_pkg::CVA6ConfigDcacheIdWidth; + + localparam int unsigned WT_DCACHE_WBUF_DEPTH = cva6_config_pkg::CVA6ConfigWtDcacheWbufDepth; + + // --------------- + // EX Stage + // --------------- + + typedef enum logic [7:0] { // basic ALU op + ADD, + SUB, + ADDW, + SUBW, + // logic operations + XORL, + ORL, + ANDL, + // shifts + SRA, + SRL, + SLL, + SRLW, + SLLW, + SRAW, + // comparisons + LTS, + LTU, + GES, + GEU, + EQ, + NE, + // jumps + JALR, + BRANCH, + // set lower than operations + SLTS, + SLTU, + // CSR functions + MRET, + SRET, + DRET, + ECALL, + WFI, + FENCE, + FENCE_I, + SFENCE_VMA, + CSR_WRITE, + CSR_READ, + CSR_SET, + CSR_CLEAR, + // LSU functions + LD, + SD, + LW, + LWU, + SW, + LH, + LHU, + SH, + LB, + SB, + LBU, + // Atomic Memory Operations + AMO_LRW, + AMO_LRD, + AMO_SCW, + AMO_SCD, + AMO_SWAPW, + AMO_ADDW, + AMO_ANDW, + AMO_ORW, + AMO_XORW, + AMO_MAXW, + AMO_MAXWU, + AMO_MINW, + AMO_MINWU, + AMO_SWAPD, + AMO_ADDD, + AMO_ANDD, + AMO_ORD, + AMO_XORD, + AMO_MAXD, + AMO_MAXDU, + AMO_MIND, + AMO_MINDU, + // Multiplications + MUL, + MULH, + MULHU, + MULHSU, + MULW, + // Divisions + DIV, + DIVU, + DIVW, + DIVUW, + REM, + REMU, + REMW, + REMUW, + // Floating-Point Load and Store Instructions + FLD, + FLW, + FLH, + FLB, + FSD, + FSW, + FSH, + FSB, + // Floating-Point Computational Instructions + FADD, + FSUB, + FMUL, + FDIV, + FMIN_MAX, + FSQRT, + FMADD, + FMSUB, + FNMSUB, + FNMADD, + // Floating-Point Conversion and Move Instructions + FCVT_F2I, + FCVT_I2F, + FCVT_F2F, + FSGNJ, + FMV_F2X, + FMV_X2F, + // Floating-Point Compare Instructions + FCMP, + // Floating-Point Classify Instruction + FCLASS, + // Vectorial Floating-Point Instructions that don't directly map onto the scalar ones + VFMIN, + VFMAX, + VFSGNJ, + VFSGNJN, + VFSGNJX, + VFEQ, + VFNE, + VFLT, + VFGE, + VFLE, + VFGT, + VFCPKAB_S, + VFCPKCD_S, + VFCPKAB_D, + VFCPKCD_D, + // Offload Instructions to be directed into cv_x_if + OFFLOAD, + // Or-Combine and REV8 + ORCB, + REV8, + // Bitwise Rotation + ROL, + ROLW, + ROR, + RORI, + RORIW, + RORW, + // Sign and Zero Extend + SEXTB, + SEXTH, + ZEXTH, + // Count population + CPOP, + CPOPW, + // Count Leading/Training Zeros + CLZ, + CLZW, + CTZ, + CTZW, + // Carry less multiplication Op's + CLMUL, + CLMULH, + CLMULR, + // Single bit instructions Op's + BCLR, + BCLRI, + BEXT, + BEXTI, + BINV, + BINVI, + BSET, + BSETI, + // Integer minimum/maximum + MAX, + MAXU, + MIN, + MINU, + // Shift with Add Unsigned Word and Unsigned Word Op's (Bitmanip) + SH1ADDUW, + SH2ADDUW, + SH3ADDUW, + ADDUW, + SLLIUW, + // Shift with Add (Bitmanip) + SH1ADD, + SH2ADD, + SH3ADD, + // Bitmanip Logical with negate op (Bitmanip) + ANDN, + ORN, + XNOR, + // Accelerator operations + ACCEL_OP, + ACCEL_OP_FS1, + ACCEL_OP_FD, + ACCEL_OP_LOAD, + ACCEL_OP_STORE, + // Zicond instruction + CZERO_EQZ, + CZERO_NEZ + } fu_op; + + typedef struct packed { + fu_t fu; + fu_op operation; + riscv::xlen_t operand_a; + riscv::xlen_t operand_b; + riscv::xlen_t imm; + logic [TRANS_ID_BITS-1:0] trans_id; + } fu_data_t; + + function automatic logic op_is_branch(input fu_op op); + unique case (op) inside + EQ, NE, LTS, GES, LTU, GEU: return 1'b1; + default: return 1'b0; // all other ops + endcase + endfunction + + // ------------------------------- + // Extract Src/Dst FP Reg from Op + // ------------------------------- + // function used in instr_trace svh + // is_rs1_fpr function is kept to allow cva6 compilation with instr_trace feature + function automatic logic is_rs1_fpr(input fu_op op); + unique case (op) inside + [FMUL : FNMADD], // Computational Operations (except ADD/SUB) + FCVT_F2I, // Float-Int Casts + FCVT_F2F, // Float-Float Casts + FSGNJ, // Sign Injections + FMV_F2X, // FPR-GPR Moves + FCMP, // Comparisons + FCLASS, // Classifications + [VFMIN : VFCPKCD_D], // Additional Vectorial FP ops + ACCEL_OP_FS1: + return 1'b1; // Accelerator instructions + default: return 1'b0; // all other ops + endcase + endfunction + + // function used in instr_trace svh + // is_rs2_fpr function is kept to allow cva6 compilation with instr_trace feature + function automatic logic is_rs2_fpr(input fu_op op); + unique case (op) inside + [FSD : FSB], // FP Stores + [FADD : FMIN_MAX], // Computational Operations (no sqrt) + [FMADD : FNMADD], // Fused Computational Operations + FCVT_F2F, // Vectorial F2F Conversions requrie target + [FSGNJ : FMV_F2X], // Sign Injections and moves mapped to SGNJ + FCMP, // Comparisons + [VFMIN : VFCPKCD_D]: + return 1'b1; // Additional Vectorial FP ops + default: return 1'b0; // all other ops + endcase + endfunction + + // function used in instr_trace svh + // is_imm_fpr function is kept to allow cva6 compilation with instr_trace feature + // ternary operations encode the rs3 address in the imm field, also add/sub + function automatic logic is_imm_fpr(input fu_op op); + unique case (op) inside + [FADD : FSUB], // ADD/SUB need inputs as Operand B/C + [FMADD : FNMADD], // Fused Computational Operations + [VFCPKAB_S : VFCPKCD_D]: + return 1'b1; // Vectorial FP cast and pack ops + default: return 1'b0; // all other ops + endcase + endfunction + + // function used in instr_trace svh + // is_rd_fpr function is kept to allow cva6 compilation with instr_trace feature + function automatic logic is_rd_fpr(input fu_op op); + unique case (op) inside + [FLD : FLB], // FP Loads + [FADD : FNMADD], // Computational Operations + FCVT_I2F, // Int-Float Casts + FCVT_F2F, // Float-Float Casts + FSGNJ, // Sign Injections + FMV_X2F, // GPR-FPR Moves + [VFMIN : VFSGNJX], // Vectorial MIN/MAX and SGNJ + [VFCPKAB_S : VFCPKCD_D], // Vectorial FP cast and pack ops + ACCEL_OP_FD: + return 1'b1; // Accelerator instructions + default: return 1'b0; // all other ops + endcase + endfunction + + function automatic logic is_amo(fu_op op); + case (op) inside + [AMO_LRW : AMO_MINDU]: begin + return 1'b1; + end + default: return 1'b0; + endcase + endfunction + + typedef struct packed { + logic valid; + logic [riscv::VLEN-1:0] vaddr; + logic overflow; + riscv::xlen_t data; + logic [(riscv::XLEN/8)-1:0] be; + fu_t fu; + fu_op operation; + logic [TRANS_ID_BITS-1:0] trans_id; + } lsu_ctrl_t; + + // --------------- + // IF/ID Stage + // --------------- + // store the decompressed instruction + typedef struct packed { + logic [riscv::VLEN-1:0] address; // the address of the instructions from below + logic [31:0] instruction; // instruction word + branchpredict_sbe_t branch_predict; // this field contains branch prediction information regarding the forward branch path + exception_t ex; // this field contains exceptions which might have happened earlier, e.g.: fetch exceptions + } fetch_entry_t; + + // --------------- + // ID/EX/WB Stage + // --------------- + + localparam RVFI = cva6_config_pkg::CVA6ConfigRvfiTrace; + + typedef struct packed { + logic [riscv::VLEN-1:0] pc; // PC of instruction + logic [TRANS_ID_BITS-1:0] trans_id; // this can potentially be simplified, we could index the scoreboard entry + // with the transaction id in any case make the width more generic + fu_t fu; // functional unit to use + fu_op op; // operation to perform in each functional unit + logic [REG_ADDR_SIZE-1:0] rs1; // register source address 1 + logic [REG_ADDR_SIZE-1:0] rs2; // register source address 2 + logic [REG_ADDR_SIZE-1:0] rd; // register destination address + riscv::xlen_t result; // for unfinished instructions this field also holds the immediate, + // for unfinished floating-point that are partly encoded in rs2, this field also holds rs2 + // for unfinished floating-point fused operations (FMADD, FMSUB, FNMADD, FNMSUB) + // this field holds the address of the third operand from the floating-point register file + logic valid; // is the result valid + logic use_imm; // should we use the immediate as operand b? + logic use_zimm; // use zimm as operand a + logic use_pc; // set if we need to use the PC as operand a, PC from exception + exception_t ex; // exception has occurred + branchpredict_sbe_t bp; // branch predict scoreboard data structure + logic is_compressed; // signals a compressed instructions, we need this information at the commit stage if + // we want jump accordingly e.g.: +4, +2 + riscv::xlen_t rs1_rdata; // information needed by RVFI + riscv::xlen_t rs2_rdata; // information needed by RVFI + logic [riscv::VLEN-1:0] lsu_addr; // information needed by RVFI + logic [(riscv::XLEN/8)-1:0] lsu_rmask; // information needed by RVFI + logic [(riscv::XLEN/8)-1:0] lsu_wmask; // information needed by RVFI + riscv::xlen_t lsu_wdata; // information needed by RVFI + logic vfp; // is this a vector floating-point instruction? + } scoreboard_entry_t; + + // --------------- + // MMU instanciation + // --------------- + localparam bit MMU_PRESENT = cva6_config_pkg::CVA6ConfigMmuPresent; + + localparam int unsigned INSTR_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigInstrTlbEntries; + localparam int unsigned DATA_TLB_ENTRIES = cva6_config_pkg::CVA6ConfigDataTlbEntries; + + // ------------------- + // Performance counter + // ------------------- + localparam bit PERF_COUNTER_EN = cva6_config_pkg::CVA6ConfigPerfCounterEn; + localparam int unsigned MHPMCounterNum = 6; + + // -------------------- + // Atomics + // -------------------- + typedef enum logic [3:0] { + AMO_NONE = 4'b0000, + AMO_LR = 4'b0001, + AMO_SC = 4'b0010, + AMO_SWAP = 4'b0011, + AMO_ADD = 4'b0100, + AMO_AND = 4'b0101, + AMO_OR = 4'b0110, + AMO_XOR = 4'b0111, + AMO_MAX = 4'b1000, + AMO_MAXU = 4'b1001, + AMO_MIN = 4'b1010, + AMO_MINU = 4'b1011, + AMO_CAS1 = 4'b1100, // unused, not part of riscv spec, but provided in OpenPiton + AMO_CAS2 = 4'b1101 // unused, not part of riscv spec, but provided in OpenPiton + } amo_t; + + typedef struct packed { + logic valid; // valid flag + logic is_2M; // + logic is_1G; // + logic [27-1:0] vpn; // VPN (39bits) = 27bits + 12bits offset + logic [ASID_WIDTH-1:0] asid; + riscv::pte_t content; + } tlb_update_t; + + // Bits required for representation of physical address space as 4K pages + // (e.g. 27*4K == 39bit address space). + localparam PPN4K_WIDTH = 38; + + typedef struct packed { + logic valid; // valid flag + logic is_4M; // + logic [20-1:0] vpn; //VPN (32bits) = 20bits + 12bits offset + logic [9-1:0] asid; //ASID length = 9 for Sv32 mmu + riscv::pte_sv32_t content; + } tlb_update_sv32_t; + + typedef enum logic [1:0] { + FE_NONE, + FE_INSTR_ACCESS_FAULT, + FE_INSTR_PAGE_FAULT + } frontend_exception_t; + + // ---------------------- + // cache request ports + // ---------------------- + // I$ address translation requests + typedef struct packed { + logic fetch_valid; // address translation valid + logic [riscv::PLEN-1:0] fetch_paddr; // physical address in + exception_t fetch_exception; // exception occurred during fetch + } icache_areq_t; + + typedef struct packed { + logic fetch_req; // address translation request + logic [riscv::VLEN-1:0] fetch_vaddr; // virtual address out + } icache_arsp_t; + + // I$ data requests + typedef struct packed { + logic req; // we request a new word + logic kill_s1; // kill the current request + logic kill_s2; // kill the last request + logic spec; // request is speculative + logic [riscv::VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup + } icache_dreq_t; + + typedef struct packed { + logic ready; // icache is ready + logic valid; // signals a valid read + logic [FETCH_WIDTH-1:0] data; // 2+ cycle out: tag + logic [FETCH_USER_WIDTH-1:0] user; // User bits + logic [riscv::VLEN-1:0] vaddr; // virtual address out + exception_t ex; // we've encountered an exception + } icache_drsp_t; + + // AMO request going to cache. this request is unconditionally valid as soon + // as request goes high. + // Furthermore, those signals are kept stable until the response indicates + // completion by asserting ack. + typedef struct packed { + logic req; // this request is valid + amo_t amo_op; // atomic memory operation to perform + logic [1:0] size; // 2'b10 --> word operation, 2'b11 --> double word operation + logic [63:0] operand_a; // address + logic [63:0] operand_b; // data as layouted in the register + } amo_req_t; + + // AMO response coming from cache. + typedef struct packed { + logic ack; // response is valid + logic [63:0] result; // sign-extended, result + } amo_resp_t; + + // D$ data requests + typedef struct packed { + logic [DCACHE_INDEX_WIDTH-1:0] address_index; + logic [DCACHE_TAG_WIDTH-1:0] address_tag; + riscv::xlen_t data_wdata; + logic [DCACHE_USER_WIDTH-1:0] data_wuser; + logic data_req; + logic data_we; + logic [(riscv::XLEN/8)-1:0] data_be; + logic [1:0] data_size; + logic [DCACHE_TID_WIDTH-1:0] data_id; + logic kill_req; + logic tag_valid; + } dcache_req_i_t; + + typedef struct packed { + logic data_gnt; + logic data_rvalid; + logic [DCACHE_TID_WIDTH-1:0] data_rid; + riscv::xlen_t data_rdata; + logic [DCACHE_USER_WIDTH-1:0] data_ruser; + } dcache_req_o_t; + + // ---------------------- + // Arithmetic Functions + // ---------------------- + function automatic riscv::xlen_t sext32(logic [31:0] operand); + return {{riscv::XLEN - 32{operand[31]}}, operand[31:0]}; + endfunction + + // ---------------------- + // Immediate functions + // ---------------------- + function automatic logic [riscv::VLEN-1:0] uj_imm(logic [31:0] instruction_i); + return { + {44 + riscv::VLEN - 64{instruction_i[31]}}, + instruction_i[19:12], + instruction_i[20], + instruction_i[30:21], + 1'b0 + }; + endfunction + + function automatic logic [riscv::VLEN-1:0] i_imm(logic [31:0] instruction_i); + return {{52 + riscv::VLEN - 64{instruction_i[31]}}, instruction_i[31:20]}; + endfunction + + function automatic logic [riscv::VLEN-1:0] sb_imm(logic [31:0] instruction_i); + return { + {51 + riscv::VLEN - 64{instruction_i[31]}}, + instruction_i[31], + instruction_i[7], + instruction_i[30:25], + instruction_i[11:8], + 1'b0 + }; + endfunction + + // ---------------------- + // LSU Functions + // ---------------------- + // align data to address e.g.: shift data to be naturally 64 + function automatic riscv::xlen_t data_align(logic [2:0] addr, logic [63:0] data); + // Set addr[2] to 1'b0 when 32bits + logic [ 2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]}; + logic [63:0] data_tmp = {64{1'b0}}; + case (addr_tmp) + 3'b000: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-1:0]}; + 3'b001: + data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]}; + 3'b010: + data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]}; + 3'b011: + data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]}; + 3'b100: data_tmp = {data[31:0], data[63:32]}; + 3'b101: data_tmp = {data[23:0], data[63:24]}; + 3'b110: data_tmp = {data[15:0], data[63:16]}; + 3'b111: data_tmp = {data[7:0], data[63:8]}; + endcase + return data_tmp[riscv::XLEN-1:0]; + endfunction + + // generate byte enable mask + function automatic logic [7:0] be_gen(logic [2:0] addr, logic [1:0] size); + case (size) + 2'b11: begin + return 8'b1111_1111; + end + 2'b10: begin + case (addr[2:0]) + 3'b000: return 8'b0000_1111; + 3'b001: return 8'b0001_1110; + 3'b010: return 8'b0011_1100; + 3'b011: return 8'b0111_1000; + 3'b100: return 8'b1111_0000; + default: ; // Do nothing + endcase + end + 2'b01: begin + case (addr[2:0]) + 3'b000: return 8'b0000_0011; + 3'b001: return 8'b0000_0110; + 3'b010: return 8'b0000_1100; + 3'b011: return 8'b0001_1000; + 3'b100: return 8'b0011_0000; + 3'b101: return 8'b0110_0000; + 3'b110: return 8'b1100_0000; + default: ; // Do nothing + endcase + end + 2'b00: begin + case (addr[2:0]) + 3'b000: return 8'b0000_0001; + 3'b001: return 8'b0000_0010; + 3'b010: return 8'b0000_0100; + 3'b011: return 8'b0000_1000; + 3'b100: return 8'b0001_0000; + 3'b101: return 8'b0010_0000; + 3'b110: return 8'b0100_0000; + 3'b111: return 8'b1000_0000; + endcase + end + endcase + return 8'b0; + endfunction + + function automatic logic [3:0] be_gen_32(logic [1:0] addr, logic [1:0] size); + case (size) + 2'b10: begin + return 4'b1111; + end + 2'b01: begin + case (addr[1:0]) + 2'b00: return 4'b0011; + 2'b01: return 4'b0110; + 2'b10: return 4'b1100; + default: ; // Do nothing + endcase + end + 2'b00: begin + case (addr[1:0]) + 2'b00: return 4'b0001; + 2'b01: return 4'b0010; + 2'b10: return 4'b0100; + 2'b11: return 4'b1000; + endcase + end + default: return 4'b0; + endcase + return 4'b0; + endfunction + + // ---------------------- + // Extract Bytes from Op + // ---------------------- + function automatic logic [1:0] extract_transfer_size(fu_op op); + case (op) + LD, SD, FLD, FSD, + AMO_LRD, AMO_SCD, + AMO_SWAPD, AMO_ADDD, + AMO_ANDD, AMO_ORD, + AMO_XORD, AMO_MAXD, + AMO_MAXDU, AMO_MIND, + AMO_MINDU: begin + return 2'b11; + end + LW, LWU, SW, FLW, FSW, + AMO_LRW, AMO_SCW, + AMO_SWAPW, AMO_ADDW, + AMO_ANDW, AMO_ORW, + AMO_XORW, AMO_MAXW, + AMO_MAXWU, AMO_MINW, + AMO_MINWU: begin + return 2'b10; + end + LH, LHU, SH, FLH, FSH: return 2'b01; + LB, LBU, SB, FLB, FSB: return 2'b00; + default: return 2'b11; + endcase + endfunction endpackage diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 67aa084569..2a9d919c1a 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -15,841 +15,835 @@ * Description: Common RISC-V definitions. */ - package riscv; - - // ---------------------- - // Import cva6 config from cva6_config_pkg - // ---------------------- - localparam XLEN = cva6_config_pkg::CVA6ConfigXlen; - localparam FPU_EN = cva6_config_pkg::CVA6ConfigFpuEn; - - // ---------------------- - // Data and Address length - // ---------------------- - typedef enum logic [3:0] { +package riscv; + + // ---------------------- + // Import cva6 config from cva6_config_pkg + // ---------------------- + localparam XLEN = cva6_config_pkg::CVA6ConfigXlen; + localparam FPU_EN = cva6_config_pkg::CVA6ConfigFpuEn; + + // ---------------------- + // Data and Address length + // ---------------------- + typedef enum logic [3:0] { ModeOff = 0, ModeSv32 = 1, ModeSv39 = 8, ModeSv48 = 9, ModeSv57 = 10, ModeSv64 = 11 - } vm_mode_t; - - // Warning: When using STD_CACHE, configuration must be PLEN=56 and VLEN=64 - // Warning: VLEN must be superior or equal to PLEN - localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length - localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length - - localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; - localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; - localparam ModeW = (XLEN == 32) ? 1 : 4; - localparam ASIDW = (XLEN == 32) ? 9 : 16; - localparam PPNW = (XLEN == 32) ? 22 : 44; - localparam vm_mode_t MODE_SV = (XLEN == 32) ? ModeSv32 : ModeSv39; - localparam SV = (MODE_SV == ModeSv32) ? 32 : 39; - localparam VPN2 = (VLEN-31 < 8) ? VLEN-31 : 8; - localparam XLEN_ALIGN_BYTES = $clog2(XLEN/8); - - typedef logic [XLEN-1:0] xlen_t; - - // -------------------- - // Privilege Spec - // -------------------- - typedef enum logic[1:0] { - PRIV_LVL_M = 2'b11, - PRIV_LVL_S = 2'b01, - PRIV_LVL_U = 2'b00 - } priv_lvl_t; - - // type which holds xlen - typedef enum logic [1:0] { - XLEN_32 = 2'b01, - XLEN_64 = 2'b10, - XLEN_128 = 2'b11 - } xlen_e; - - typedef enum logic [1:0] { - Off = 2'b00, - Initial = 2'b01, - Clean = 2'b10, - Dirty = 2'b11 - } xs_t; - - typedef struct packed { - logic sd; // signal dirty state - read-only - logic [62:34] wpri6; // writes preserved reads ignored - xlen_e uxl; // variable user mode xlen - hardwired to zero - logic [12:0] wpri5; // writes preserved reads ignored - logic mxr; // make executable readable - logic sum; // permit supervisor user memory access - logic wpri4; // writes preserved reads ignored - xs_t xs; // extension register - hardwired to zero - xs_t fs; // floating point extension register - logic [1:0] wpri3; // writes preserved reads ignored - xs_t vs; // vector extension register - logic spp; // holds the previous privilege mode up to supervisor - logic wpri2; // writes preserved reads ignored - logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) - logic spie; // supervisor interrupts enable bit active prior to trap - logic [1:0] wpri1; // writes preserved reads ignored - logic sie; // supervisor interrupts enable - logic wpri0; // writes preserved reads ignored - } sstatus_rv_t; - - typedef struct packed { - logic sd; // signal dirty state - read-only - logic [62:36] wpri4; // writes preserved reads ignored - xlen_e sxl; // variable supervisor mode xlen - hardwired to zero - xlen_e uxl; // variable user mode xlen - hardwired to zero - logic [8:0] wpri3; // writes preserved reads ignored - logic tsr; // trap sret - logic tw; // time wait - logic tvm; // trap virtual memory - logic mxr; // make executable readable - logic sum; // permit supervisor user memory access - logic mprv; // modify privilege - privilege level for ld/st - xs_t xs; // extension register - hardwired to zero - xs_t fs; // floating point extension register - priv_lvl_t mpp; // holds the previous privilege mode up to machine - xs_t vs; // vector extension register - logic spp; // holds the previous privilege mode up to supervisor - logic mpie; // machine interrupts enable bit active prior to trap - logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) - logic spie; // supervisor interrupts enable bit active prior to trap - logic wpri2; // writes preserved reads ignored - logic mie; // machine interrupts enable - logic wpri1; // writes preserved reads ignored - logic sie; // supervisor interrupts enable - logic wpri0; // writes preserved reads ignored - } mstatus_rv_t; - - typedef struct packed { - logic [ModeW-1:0] mode; - logic [ASIDW-1:0] asid; - logic [PPNW-1:0] ppn; - } satp_t; - - // -------------------- - // Instruction Types - // -------------------- - typedef struct packed { - logic [31:25] funct7; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } rtype_t; - - typedef struct packed { - logic [31:27] rs3; - logic [26:25] funct2; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } r4type_t; - - typedef struct packed { - logic [31:27] funct5; - logic [26:25] fmt; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] rm; - logic [11:7] rd; - logic [6:0] opcode; - } rftype_t; // floating-point - - typedef struct packed { - logic [31:30] funct2; - logic [29:25] vecfltop; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:14] repl; - logic [13:12] vfmt; - logic [11:7] rd; - logic [6:0] opcode; - } rvftype_t; // vectorial floating-point - - typedef struct packed { - logic [31:20] imm; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } itype_t; - - typedef struct packed { - logic [31:25] imm; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] imm0; - logic [6:0] opcode; - } stype_t; - - typedef struct packed { - logic [31:12] imm; - logic [11:7] rd; - logic [6:0] opcode; - } utype_t; - - // atomic instructions - typedef struct packed { - logic [31:27] funct5; - logic aq; - logic rl; - logic [24:20] rs2; - logic [19:15] rs1; - logic [14:12] funct3; - logic [11:7] rd; - logic [6:0] opcode; - } atype_t; - - typedef union packed { - logic [31:0] instr; - rtype_t rtype; - r4type_t r4type; - rftype_t rftype; - rvftype_t rvftype; - itype_t itype; - stype_t stype; - utype_t utype; - atype_t atype; - } instruction_t; - - // -------------------- - // Opcodes - // -------------------- - // RV32/64G listings: - // Quadrant 0 - localparam OpcodeLoad = 7'b00_000_11; - localparam OpcodeLoadFp = 7'b00_001_11; - localparam OpcodeCustom0 = 7'b00_010_11; - localparam OpcodeMiscMem = 7'b00_011_11; - localparam OpcodeOpImm = 7'b00_100_11; - localparam OpcodeAuipc = 7'b00_101_11; - localparam OpcodeOpImm32 = 7'b00_110_11; - // Quadrant 1 - localparam OpcodeStore = 7'b01_000_11; - localparam OpcodeStoreFp = 7'b01_001_11; - localparam OpcodeCustom1 = 7'b01_010_11; - localparam OpcodeAmo = 7'b01_011_11; - localparam OpcodeOp = 7'b01_100_11; - localparam OpcodeLui = 7'b01_101_11; - localparam OpcodeOp32 = 7'b01_110_11; - // Quadrant 2 - localparam OpcodeMadd = 7'b10_000_11; - localparam OpcodeMsub = 7'b10_001_11; - localparam OpcodeNmsub = 7'b10_010_11; - localparam OpcodeNmadd = 7'b10_011_11; - localparam OpcodeOpFp = 7'b10_100_11; - localparam OpcodeVec = 7'b10_101_11; - localparam OpcodeCustom2 = 7'b10_110_11; - // Quadrant 3 - localparam OpcodeBranch = 7'b11_000_11; - localparam OpcodeJalr = 7'b11_001_11; - localparam OpcodeRsrvd2 = 7'b11_010_11; - localparam OpcodeJal = 7'b11_011_11; - localparam OpcodeSystem = 7'b11_100_11; - localparam OpcodeRsrvd3 = 7'b11_101_11; - localparam OpcodeCustom3 = 7'b11_110_11; - - // RV64C/RV32C listings: - // Quadrant 0 - localparam OpcodeC0 = 2'b00; - localparam OpcodeC0Addi4spn = 3'b000; - localparam OpcodeC0Fld = 3'b001; - localparam OpcodeC0Lw = 3'b010; - localparam OpcodeC0Ld = 3'b011; - localparam OpcodeC0Rsrvd = 3'b100; - localparam OpcodeC0Fsd = 3'b101; - localparam OpcodeC0Sw = 3'b110; - localparam OpcodeC0Sd = 3'b111; - // Quadrant 1 - localparam OpcodeC1 = 2'b01; - localparam OpcodeC1Addi = 3'b000; - localparam OpcodeC1Addiw = 3'b001; //for RV64I only - localparam OpcodeC1Jal = 3'b001; //for RV32I only - localparam OpcodeC1Li = 3'b010; - localparam OpcodeC1LuiAddi16sp = 3'b011; - localparam OpcodeC1MiscAlu = 3'b100; - localparam OpcodeC1J = 3'b101; - localparam OpcodeC1Beqz = 3'b110; - localparam OpcodeC1Bnez = 3'b111; - // Quadrant 2 - localparam OpcodeC2 = 2'b10; - localparam OpcodeC2Slli = 3'b000; - localparam OpcodeC2Fldsp = 3'b001; - localparam OpcodeC2Lwsp = 3'b010; - localparam OpcodeC2Ldsp = 3'b011; - localparam OpcodeC2JalrMvAdd = 3'b100; - localparam OpcodeC2Fsdsp = 3'b101; - localparam OpcodeC2Swsp = 3'b110; - localparam OpcodeC2Sdsp = 3'b111; - - // ---------------------- - // Virtual Memory - // ---------------------- - // memory management, pte for sv39 - typedef struct packed { - logic [9:0] reserved; - logic [44-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; - } pte_t; - - // memory management, pte for sv32 - typedef struct packed { - logic [22-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; - } pte_sv32_t; - - // memory management, pte for cva6 - typedef struct packed { - logic [PPNW-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; - } pte_cva6_t; - - // ---------------------- - // Exception Cause Codes - // ---------------------- - localparam logic [XLEN-1:0] INSTR_ADDR_MISALIGNED = 0; - localparam logic [XLEN-1:0] INSTR_ACCESS_FAULT = 1; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ILLEGAL_INSTR = 2; - localparam logic [XLEN-1:0] BREAKPOINT = 3; - localparam logic [XLEN-1:0] LD_ADDR_MISALIGNED = 4; - localparam logic [XLEN-1:0] LD_ACCESS_FAULT = 5; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ST_ADDR_MISALIGNED = 6; - localparam logic [XLEN-1:0] ST_ACCESS_FAULT = 7; // Illegal access as governed by PMPs and PMAs - localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode - localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from supervisor mode - localparam logic [XLEN-1:0] ENV_CALL_MMODE = 11; // environment call from machine mode - localparam logic [XLEN-1:0] INSTR_PAGE_FAULT = 12; // Instruction page fault - localparam logic [XLEN-1:0] LOAD_PAGE_FAULT = 13; // Load page fault - localparam logic [XLEN-1:0] STORE_PAGE_FAULT = 15; // Store page fault - localparam logic [XLEN-1:0] DEBUG_REQUEST = 24; // Debug request - - localparam int unsigned IRQ_S_SOFT = 1; - localparam int unsigned IRQ_M_SOFT = 3; - localparam int unsigned IRQ_S_TIMER = 5; - localparam int unsigned IRQ_M_TIMER = 7; - localparam int unsigned IRQ_S_EXT = 9; - localparam int unsigned IRQ_M_EXT = 11; - - localparam logic [XLEN-1:0] MIP_SSIP = 1 << IRQ_S_SOFT; - localparam logic [XLEN-1:0] MIP_MSIP = 1 << IRQ_M_SOFT; - localparam logic [XLEN-1:0] MIP_STIP = 1 << IRQ_S_TIMER; - localparam logic [XLEN-1:0] MIP_MTIP = 1 << IRQ_M_TIMER; - localparam logic [XLEN-1:0] MIP_SEIP = 1 << IRQ_S_EXT; - localparam logic [XLEN-1:0] MIP_MEIP = 1 << IRQ_M_EXT; - - localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_SOFT); - localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_SOFT); - localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_TIMER); - localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_TIMER); - localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_EXT); - localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_EXT); - - // ----- - // CSRs - // ----- - typedef enum logic [11:0] { - // Floating-Point CSRs - CSR_FFLAGS = 12'h001, - CSR_FRM = 12'h002, - CSR_FCSR = 12'h003, - CSR_FTRAN = 12'h800, - // Vector CSRs - CSR_VSTART = 12'h008, - CSR_VXSAT = 12'h009, - CSR_VXRM = 12'h00A, - CSR_VCSR = 12'h00F, - CSR_VL = 12'hC20, - CSR_VTYPE = 12'hC21, - CSR_VLENB = 12'hC22, - // Supervisor Mode CSRs - CSR_SSTATUS = 12'h100, - CSR_SIE = 12'h104, - CSR_STVEC = 12'h105, - CSR_SCOUNTEREN = 12'h106, - CSR_SSCRATCH = 12'h140, - CSR_SEPC = 12'h141, - CSR_SCAUSE = 12'h142, - CSR_STVAL = 12'h143, - CSR_SIP = 12'h144, - CSR_SATP = 12'h180, - // Machine Mode CSRs - CSR_MSTATUS = 12'h300, - CSR_MISA = 12'h301, - CSR_MEDELEG = 12'h302, - CSR_MIDELEG = 12'h303, - CSR_MIE = 12'h304, - CSR_MTVEC = 12'h305, - CSR_MCOUNTEREN = 12'h306, - CSR_MCOUNTINHIBIT = 12'h320, - CSR_MHPM_EVENT_3 = 12'h323, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_4 = 12'h324, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_5 = 12'h325, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_6 = 12'h326, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_7 = 12'h327, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_8 = 12'h328, //Machine performance monitoring Event Selector - CSR_MHPM_EVENT_9 = 12'h329, //Reserved - CSR_MHPM_EVENT_10 = 12'h32A, //Reserved - CSR_MHPM_EVENT_11 = 12'h32B, //Reserved - CSR_MHPM_EVENT_12 = 12'h32C, //Reserved - CSR_MHPM_EVENT_13 = 12'h32D, //Reserved - CSR_MHPM_EVENT_14 = 12'h32E, //Reserved - CSR_MHPM_EVENT_15 = 12'h32F, //Reserved - CSR_MHPM_EVENT_16 = 12'h330, //Reserved - CSR_MHPM_EVENT_17 = 12'h331, //Reserved - CSR_MHPM_EVENT_18 = 12'h332, //Reserved - CSR_MHPM_EVENT_19 = 12'h333, //Reserved - CSR_MHPM_EVENT_20 = 12'h334, //Reserved - CSR_MHPM_EVENT_21 = 12'h335, //Reserved - CSR_MHPM_EVENT_22 = 12'h336, //Reserved - CSR_MHPM_EVENT_23 = 12'h337, //Reserved - CSR_MHPM_EVENT_24 = 12'h338, //Reserved - CSR_MHPM_EVENT_25 = 12'h339, //Reserved - CSR_MHPM_EVENT_26 = 12'h33A, //Reserved - CSR_MHPM_EVENT_27 = 12'h33B, //Reserved - CSR_MHPM_EVENT_28 = 12'h33C, //Reserved - CSR_MHPM_EVENT_29 = 12'h33D, //Reserved - CSR_MHPM_EVENT_30 = 12'h33E, //Reserved - CSR_MHPM_EVENT_31 = 12'h33F, //Reserved - CSR_MSCRATCH = 12'h340, - CSR_MEPC = 12'h341, - CSR_MCAUSE = 12'h342, - CSR_MTVAL = 12'h343, - CSR_MIP = 12'h344, - CSR_PMPCFG0 = 12'h3A0, - CSR_PMPCFG1 = 12'h3A1, - CSR_PMPCFG2 = 12'h3A2, - CSR_PMPCFG3 = 12'h3A3, - CSR_PMPADDR0 = 12'h3B0, - CSR_PMPADDR1 = 12'h3B1, - CSR_PMPADDR2 = 12'h3B2, - CSR_PMPADDR3 = 12'h3B3, - CSR_PMPADDR4 = 12'h3B4, - CSR_PMPADDR5 = 12'h3B5, - CSR_PMPADDR6 = 12'h3B6, - CSR_PMPADDR7 = 12'h3B7, - CSR_PMPADDR8 = 12'h3B8, - CSR_PMPADDR9 = 12'h3B9, - CSR_PMPADDR10 = 12'h3BA, - CSR_PMPADDR11 = 12'h3BB, - CSR_PMPADDR12 = 12'h3BC, - CSR_PMPADDR13 = 12'h3BD, - CSR_PMPADDR14 = 12'h3BE, - CSR_PMPADDR15 = 12'h3BF, - CSR_MVENDORID = 12'hF11, - CSR_MARCHID = 12'hF12, - CSR_MIMPID = 12'hF13, - CSR_MHARTID = 12'hF14, - CSR_MCYCLE = 12'hB00, - CSR_MCYCLEH = 12'hB80, - CSR_MINSTRET = 12'hB02, - CSR_MINSTRETH = 12'hB82, - //Performance Counters - CSR_MHPM_COUNTER_3 = 12'hB03, - CSR_MHPM_COUNTER_4 = 12'hB04, - CSR_MHPM_COUNTER_5 = 12'hB05, - CSR_MHPM_COUNTER_6 = 12'hB06, - CSR_MHPM_COUNTER_7 = 12'hB07, - CSR_MHPM_COUNTER_8 = 12'hB08, - CSR_MHPM_COUNTER_9 = 12'hB09, // reserved - CSR_MHPM_COUNTER_10 = 12'hB0A, // reserved - CSR_MHPM_COUNTER_11 = 12'hB0B, // reserved - CSR_MHPM_COUNTER_12 = 12'hB0C, // reserved - CSR_MHPM_COUNTER_13 = 12'hB0D, // reserved - CSR_MHPM_COUNTER_14 = 12'hB0E, // reserved - CSR_MHPM_COUNTER_15 = 12'hB0F, // reserved - CSR_MHPM_COUNTER_16 = 12'hB10, // reserved - CSR_MHPM_COUNTER_17 = 12'hB11, // reserved - CSR_MHPM_COUNTER_18 = 12'hB12, // reserved - CSR_MHPM_COUNTER_19 = 12'hB13, // reserved - CSR_MHPM_COUNTER_20 = 12'hB14, // reserved - CSR_MHPM_COUNTER_21 = 12'hB15, // reserved - CSR_MHPM_COUNTER_22 = 12'hB16, // reserved - CSR_MHPM_COUNTER_23 = 12'hB17, // reserved - CSR_MHPM_COUNTER_24 = 12'hB18, // reserved - CSR_MHPM_COUNTER_25 = 12'hB19, // reserved - CSR_MHPM_COUNTER_26 = 12'hB1A, // reserved - CSR_MHPM_COUNTER_27 = 12'hB1B, // reserved - CSR_MHPM_COUNTER_28 = 12'hB1C, // reserved - CSR_MHPM_COUNTER_29 = 12'hB1D, // reserved - CSR_MHPM_COUNTER_30 = 12'hB1E, // reserved - CSR_MHPM_COUNTER_31 = 12'hB1F, // reserved - CSR_MHPM_COUNTER_3H = 12'hB83, - CSR_MHPM_COUNTER_4H = 12'hB84, - CSR_MHPM_COUNTER_5H = 12'hB85, - CSR_MHPM_COUNTER_6H = 12'hB86, - CSR_MHPM_COUNTER_7H = 12'hB87, - CSR_MHPM_COUNTER_8H = 12'hB88, - CSR_MHPM_COUNTER_9H = 12'hB89, // reserved - CSR_MHPM_COUNTER_10H = 12'hB8A, // reserved - CSR_MHPM_COUNTER_11H = 12'hB8B, // reserved - CSR_MHPM_COUNTER_12H = 12'hB8C, // reserved - CSR_MHPM_COUNTER_13H = 12'hB8D, // reserved - CSR_MHPM_COUNTER_14H = 12'hB8E, // reserved - CSR_MHPM_COUNTER_15H = 12'hB8F, // reserved - CSR_MHPM_COUNTER_16H = 12'hB90, // reserved - CSR_MHPM_COUNTER_17H = 12'hB91, // reserved - CSR_MHPM_COUNTER_18H = 12'hB92, // reserved - CSR_MHPM_COUNTER_19H = 12'hB93, // reserved - CSR_MHPM_COUNTER_20H = 12'hB94, // reserved - CSR_MHPM_COUNTER_21H = 12'hB95, // reserved - CSR_MHPM_COUNTER_22H = 12'hB96, // reserved - CSR_MHPM_COUNTER_23H = 12'hB97, // reserved - CSR_MHPM_COUNTER_24H = 12'hB98, // reserved - CSR_MHPM_COUNTER_25H = 12'hB99, // reserved - CSR_MHPM_COUNTER_26H = 12'hB9A, // reserved - CSR_MHPM_COUNTER_27H = 12'hB9B, // reserved - CSR_MHPM_COUNTER_28H = 12'hB9C, // reserved - CSR_MHPM_COUNTER_29H = 12'hB9D, // reserved - CSR_MHPM_COUNTER_30H = 12'hB9E, // reserved - CSR_MHPM_COUNTER_31H = 12'hB9F, // reserved - // Cache Control (platform specifc) - CSR_DCACHE = 12'h7C1, - CSR_ICACHE = 12'h7C0, - // Accelerator memory consistency (platform specific) - CSR_ACC_CONS = 12'h7C2, - // Triggers - CSR_TSELECT = 12'h7A0, - CSR_TDATA1 = 12'h7A1, - CSR_TDATA2 = 12'h7A2, - CSR_TDATA3 = 12'h7A3, - CSR_TINFO = 12'h7A4, - // Debug CSR - CSR_DCSR = 12'h7b0, - CSR_DPC = 12'h7b1, - CSR_DSCRATCH0 = 12'h7b2, // optional - CSR_DSCRATCH1 = 12'h7b3, // optional - // Counters and Timers (User Mode - R/O Shadows) - CSR_CYCLE = 12'hC00, - CSR_CYCLEH = 12'hC80, - CSR_TIME = 12'hC01, - CSR_TIMEH = 12'hC81, - CSR_INSTRET = 12'hC02, - CSR_INSTRETH = 12'hC82, - // Performance counters (User Mode - R/O Shadows) - CSR_HPM_COUNTER_3 = 12'hC03, - CSR_HPM_COUNTER_4 = 12'hC04, - CSR_HPM_COUNTER_5 = 12'hC05, - CSR_HPM_COUNTER_6 = 12'hC06, - CSR_HPM_COUNTER_7 = 12'hC07, - CSR_HPM_COUNTER_8 = 12'hC08, - CSR_HPM_COUNTER_9 = 12'hC09, // reserved - CSR_HPM_COUNTER_10 = 12'hC0A, // reserved - CSR_HPM_COUNTER_11 = 12'hC0B, // reserved - CSR_HPM_COUNTER_12 = 12'hC0C, // reserved - CSR_HPM_COUNTER_13 = 12'hC0D, // reserved - CSR_HPM_COUNTER_14 = 12'hC0E, // reserved - CSR_HPM_COUNTER_15 = 12'hC0F, // reserved - CSR_HPM_COUNTER_16 = 12'hC10, // reserved - CSR_HPM_COUNTER_17 = 12'hC11, // reserved - CSR_HPM_COUNTER_18 = 12'hC12, // reserved - CSR_HPM_COUNTER_19 = 12'hC13, // reserved - CSR_HPM_COUNTER_20 = 12'hC14, // reserved - CSR_HPM_COUNTER_21 = 12'hC15, // reserved - CSR_HPM_COUNTER_22 = 12'hC16, // reserved - CSR_HPM_COUNTER_23 = 12'hC17, // reserved - CSR_HPM_COUNTER_24 = 12'hC18, // reserved - CSR_HPM_COUNTER_25 = 12'hC19, // reserved - CSR_HPM_COUNTER_26 = 12'hC1A, // reserved - CSR_HPM_COUNTER_27 = 12'hC1B, // reserved - CSR_HPM_COUNTER_28 = 12'hC1C, // reserved - CSR_HPM_COUNTER_29 = 12'hC1D, // reserved - CSR_HPM_COUNTER_30 = 12'hC1E, // reserved - CSR_HPM_COUNTER_31 = 12'hC1F, // reserved - CSR_HPM_COUNTER_3H = 12'hC83, - CSR_HPM_COUNTER_4H = 12'hC84, - CSR_HPM_COUNTER_5H = 12'hC85, - CSR_HPM_COUNTER_6H = 12'hC86, - CSR_HPM_COUNTER_7H = 12'hC87, - CSR_HPM_COUNTER_8H = 12'hC88, - CSR_HPM_COUNTER_9H = 12'hC89, // reserved - CSR_HPM_COUNTER_10H = 12'hC8A, // reserved - CSR_HPM_COUNTER_11H = 12'hC8B, // reserved - CSR_HPM_COUNTER_12H = 12'hC8C, // reserved - CSR_HPM_COUNTER_13H = 12'hC8D, // reserved - CSR_HPM_COUNTER_14H = 12'hC8E, // reserved - CSR_HPM_COUNTER_15H = 12'hC8F, // reserved - CSR_HPM_COUNTER_16H = 12'hC90, // reserved - CSR_HPM_COUNTER_17H = 12'hC91, // reserved - CSR_HPM_COUNTER_18H = 12'hC92, // reserved - CSR_HPM_COUNTER_19H = 12'hC93, // reserved - CSR_HPM_COUNTER_20H = 12'hC94, // reserved - CSR_HPM_COUNTER_21H = 12'hC95, // reserved - CSR_HPM_COUNTER_22H = 12'hC96, // reserved - CSR_HPM_COUNTER_23H = 12'hC97, // reserved - CSR_HPM_COUNTER_24H = 12'hC98, // reserved - CSR_HPM_COUNTER_25H = 12'hC99, // reserved - CSR_HPM_COUNTER_26H = 12'hC9A, // reserved - CSR_HPM_COUNTER_27H = 12'hC9B, // reserved - CSR_HPM_COUNTER_28H = 12'hC9C, // reserved - CSR_HPM_COUNTER_29H = 12'hC9D, // reserved - CSR_HPM_COUNTER_30H = 12'hC9E, // reserved - CSR_HPM_COUNTER_31H = 12'hC9F // reserved - } csr_reg_t; - - localparam logic [63:0] SSTATUS_UIE = 'h00000001; - localparam logic [63:0] SSTATUS_SIE = 'h00000002; - localparam logic [63:0] SSTATUS_SPIE = 'h00000020; - localparam logic [63:0] SSTATUS_SPP = 'h00000100; - localparam logic [63:0] SSTATUS_FS = 'h00006000; - localparam logic [63:0] SSTATUS_XS = 'h00018000; - localparam logic [63:0] SSTATUS_SUM = 'h00040000; - localparam logic [63:0] SSTATUS_MXR = 'h00080000; - localparam logic [63:0] SSTATUS_UPIE = 'h00000010; - localparam logic [63:0] SSTATUS_UXL = 64'h0000000300000000; - localparam logic [63:0] SSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; - - localparam logic [63:0] MSTATUS_UIE = 'h00000001; - localparam logic [63:0] MSTATUS_SIE = 'h00000002; - localparam logic [63:0] MSTATUS_HIE = 'h00000004; - localparam logic [63:0] MSTATUS_MIE = 'h00000008; - localparam logic [63:0] MSTATUS_UPIE = 'h00000010; - localparam logic [63:0] MSTATUS_SPIE = 'h00000020; - localparam logic [63:0] MSTATUS_HPIE = 'h00000040; - localparam logic [63:0] MSTATUS_MPIE = 'h00000080; - localparam logic [63:0] MSTATUS_SPP = 'h00000100; - localparam logic [63:0] MSTATUS_HPP = 'h00000600; - localparam logic [63:0] MSTATUS_MPP = 'h00001800; - localparam logic [63:0] MSTATUS_FS = 'h00006000; - localparam logic [63:0] MSTATUS_XS = 'h00018000; - localparam logic [63:0] MSTATUS_MPRV = 'h00020000; - localparam logic [63:0] MSTATUS_SUM = 'h00040000; - localparam logic [63:0] MSTATUS_MXR = 'h00080000; - localparam logic [63:0] MSTATUS_TVM = 'h00100000; - localparam logic [63:0] MSTATUS_TW = 'h00200000; - localparam logic [63:0] MSTATUS_TSR = 'h00400000; - localparam logic [63:0] MSTATUS_UXL = {30'h0000000, IS_XLEN64, IS_XLEN64, 32'h00000000}; - localparam logic [63:0] MSTATUS_SXL = {28'h0000000, IS_XLEN64, IS_XLEN64, 34'h00000000}; - localparam logic [63:0] MSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; - - typedef enum logic [2:0] { - CSRRW = 3'h1, - CSRRS = 3'h2, - CSRRC = 3'h3, - CSRRWI = 3'h5, - CSRRSI = 3'h6, - CSRRCI = 3'h7 - } csr_op_t; - - // decoded CSR address - typedef struct packed { - logic [1:0] rw; - priv_lvl_t priv_lvl; - logic [7:0] address; - } csr_addr_t; - - typedef union packed { - csr_reg_t address; - csr_addr_t csr_decode; - } csr_t; - - // Floating-Point control and status register (32-bit!) - typedef struct packed { - logic [31:15] reserved; // reserved for L extension, return 0 otherwise - logic [6:0] fprec; // div/sqrt precision control - logic [2:0] frm; // float rounding mode - logic [4:0] fflags; // float exception flags - } fcsr_t; - - // PMP - typedef enum logic [1:0] { - OFF = 2'b00, - TOR = 2'b01, - NA4 = 2'b10, - NAPOT = 2'b11 - } pmp_addr_mode_t; - - // PMP Access Type - typedef enum logic [2:0] { - ACCESS_NONE = 3'b000, - ACCESS_READ = 3'b001, - ACCESS_WRITE = 3'b010, - ACCESS_EXEC = 3'b100 - } pmp_access_t; - - typedef struct packed { - logic x; - logic w; - logic r; - } pmpcfg_access_t; - - // packed struct of a PMP configuration register (8bit) - typedef struct packed { - logic locked; // lock this configuration - logic [1:0] reserved; - pmp_addr_mode_t addr_mode; // Off, TOR, NA4, NAPOT - pmpcfg_access_t access_type; - } pmpcfg_t; - - // ----- - // Debug - // ----- - typedef struct packed { - logic [31:28] xdebugver; - logic [27:16] zero2; - logic ebreakm; - logic zero1; - logic ebreaks; - logic ebreaku; - logic stepie; - logic stopcount; - logic stoptime; - logic [8:6] cause; - logic zero0; - logic mprven; - logic nmip; - logic step; - priv_lvl_t prv; - } dcsr_t; - - // Instruction Generation *incomplete* - function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm); - // OpCode Jal - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; - endfunction - - function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset); - // OpCode Jal - return {offset[11:0], rs1, 3'b0, rd, 7'h67}; - endfunction - - function automatic logic [31:0] andi (logic[4:0] rd, logic[4:0] rs1, logic [11:0] imm); - // OpCode andi - return {imm[11:0], rs1, 3'h7, rd, 7'h13}; - endfunction - - function automatic logic [31:0] slli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); - // OpCode slli - return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; - endfunction - - function automatic logic [31:0] srli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); - // OpCode srli - return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; - endfunction - - function automatic logic [31:0] load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); - // OpCode Load - return {offset[11:0], base, size, dest, 7'h03}; - endfunction - - function automatic logic [31:0] auipc (logic[4:0] rd, logic [20:0] imm); - // OpCode Auipc - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; - endfunction - - function automatic logic [31:0] store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); - // OpCode Store - return {offset[11:5], src, base, size, offset[4:0], 7'h23}; - endfunction - - function automatic logic [31:0] float_load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); - // OpCode Load - return {offset[11:0], base, size, dest, 7'b00_001_11}; - endfunction - - function automatic logic [31:0] float_store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); - // OpCode Store - return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; - endfunction - - function automatic logic [31:0] csrw (csr_reg_t csr, logic[4:0] rs1); - // CSRRW, rd, OpCode System - return {csr, rs1, 3'h1, 5'h0, 7'h73}; - endfunction - - function automatic logic [31:0] csrr (csr_reg_t csr, logic [4:0] dest); - // rs1, CSRRS, rd, OpCode System - return {csr, 5'h0, 3'h2, dest, 7'h73}; - endfunction - - function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, logic [11:0] offset); - // OpCode Branch - return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; - endfunction - - function automatic logic [31:0] ebreak (); - return 32'h00100073; - endfunction - - function automatic logic [31:0] wfi (); - return 32'h10500073; - endfunction - - function automatic logic [31:0] nop (); - return 32'h00000013; - endfunction - - function automatic logic [31:0] illegal (); - return 32'h00000000; - endfunction - - - // trace log compatible to spikes commit log feature - // pragma translate_off - function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr, logic [4:0] rd, logic [63:0] result, logic rd_fpr); - string rd_s; - string instr_word; - - automatic string rf_s = rd_fpr ? "f" : "x"; - - if (instr[1:0] != 2'b11) begin - instr_word = $sformatf("(0x%h)", instr[15:0]); - end else begin - instr_word = $sformatf("(0x%h)", instr); - end - - if (rd < 10) rd_s = $sformatf("%s %0d", rf_s, rd); - else rd_s = $sformatf("%s%0d", rf_s, rd); - - if (rd_fpr || rd != 0) begin - // 0 0x0000000080000118 (0xeecf8f93) x31 0x0000000080004000 - return $sformatf("%d 0x%h %s %s 0x%h\n", priv_lvl, pc, instr_word, rd_s, result); - end else begin - // 0 0x000000008000019c (0x0040006f) - return $sformatf("%d 0x%h %s\n", priv_lvl, pc, instr_word); - end - endfunction - - typedef struct { - byte priv; - longint unsigned pc; - byte is_fp; - byte rd; - longint unsigned data; - int unsigned instr; - byte was_exception; - } commit_log_t; - // pragma translate_on + } vm_mode_t; + + // Warning: When using STD_CACHE, configuration must be PLEN=56 and VLEN=64 + // Warning: VLEN must be superior or equal to PLEN + localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length + localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length + + localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; + localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; + localparam ModeW = (XLEN == 32) ? 1 : 4; + localparam ASIDW = (XLEN == 32) ? 9 : 16; + localparam PPNW = (XLEN == 32) ? 22 : 44; + localparam vm_mode_t MODE_SV = (XLEN == 32) ? ModeSv32 : ModeSv39; + localparam SV = (MODE_SV == ModeSv32) ? 32 : 39; + localparam VPN2 = (VLEN - 31 < 8) ? VLEN - 31 : 8; + localparam XLEN_ALIGN_BYTES = $clog2(XLEN / 8); + + typedef logic [XLEN-1:0] xlen_t; + + // -------------------- + // Privilege Spec + // -------------------- + typedef enum logic [1:0] { + PRIV_LVL_M = 2'b11, + PRIV_LVL_S = 2'b01, + PRIV_LVL_U = 2'b00 + } priv_lvl_t; + + // type which holds xlen + typedef enum logic [1:0] { + XLEN_32 = 2'b01, + XLEN_64 = 2'b10, + XLEN_128 = 2'b11 + } xlen_e; + + typedef enum logic [1:0] { + Off = 2'b00, + Initial = 2'b01, + Clean = 2'b10, + Dirty = 2'b11 + } xs_t; + + typedef struct packed { + logic sd; // signal dirty state - read-only + logic [62:34] wpri6; // writes preserved reads ignored + xlen_e uxl; // variable user mode xlen - hardwired to zero + logic [12:0] wpri5; // writes preserved reads ignored + logic mxr; // make executable readable + logic sum; // permit supervisor user memory access + logic wpri4; // writes preserved reads ignored + xs_t xs; // extension register - hardwired to zero + xs_t fs; // floating point extension register + logic [1:0] wpri3; // writes preserved reads ignored + xs_t vs; // vector extension register + logic spp; // holds the previous privilege mode up to supervisor + logic wpri2; // writes preserved reads ignored + logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) + logic spie; // supervisor interrupts enable bit active prior to trap + logic [1:0] wpri1; // writes preserved reads ignored + logic sie; // supervisor interrupts enable + logic wpri0; // writes preserved reads ignored + } sstatus_rv_t; + + typedef struct packed { + logic sd; // signal dirty state - read-only + logic [62:36] wpri4; // writes preserved reads ignored + xlen_e sxl; // variable supervisor mode xlen - hardwired to zero + xlen_e uxl; // variable user mode xlen - hardwired to zero + logic [8:0] wpri3; // writes preserved reads ignored + logic tsr; // trap sret + logic tw; // time wait + logic tvm; // trap virtual memory + logic mxr; // make executable readable + logic sum; // permit supervisor user memory access + logic mprv; // modify privilege - privilege level for ld/st + xs_t xs; // extension register - hardwired to zero + xs_t fs; // floating point extension register + priv_lvl_t mpp; // holds the previous privilege mode up to machine + xs_t vs; // vector extension register + logic spp; // holds the previous privilege mode up to supervisor + logic mpie; // machine interrupts enable bit active prior to trap + logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1) + logic spie; // supervisor interrupts enable bit active prior to trap + logic wpri2; // writes preserved reads ignored + logic mie; // machine interrupts enable + logic wpri1; // writes preserved reads ignored + logic sie; // supervisor interrupts enable + logic wpri0; // writes preserved reads ignored + } mstatus_rv_t; + + typedef struct packed { + logic [ModeW-1:0] mode; + logic [ASIDW-1:0] asid; + logic [PPNW-1:0] ppn; + } satp_t; + + // -------------------- + // Instruction Types + // -------------------- + typedef struct packed { + logic [31:25] funct7; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } rtype_t; + + typedef struct packed { + logic [31:27] rs3; + logic [26:25] funct2; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } r4type_t; + + typedef struct packed { + logic [31:27] funct5; + logic [26:25] fmt; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] rm; + logic [11:7] rd; + logic [6:0] opcode; + } rftype_t; // floating-point + + typedef struct packed { + logic [31:30] funct2; + logic [29:25] vecfltop; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:14] repl; + logic [13:12] vfmt; + logic [11:7] rd; + logic [6:0] opcode; + } rvftype_t; // vectorial floating-point + + typedef struct packed { + logic [31:20] imm; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } itype_t; + + typedef struct packed { + logic [31:25] imm; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] imm0; + logic [6:0] opcode; + } stype_t; + + typedef struct packed { + logic [31:12] imm; + logic [11:7] rd; + logic [6:0] opcode; + } utype_t; + + // atomic instructions + typedef struct packed { + logic [31:27] funct5; + logic aq; + logic rl; + logic [24:20] rs2; + logic [19:15] rs1; + logic [14:12] funct3; + logic [11:7] rd; + logic [6:0] opcode; + } atype_t; + + typedef union packed { + logic [31:0] instr; + rtype_t rtype; + r4type_t r4type; + rftype_t rftype; + rvftype_t rvftype; + itype_t itype; + stype_t stype; + utype_t utype; + atype_t atype; + } instruction_t; + + // -------------------- + // Opcodes + // -------------------- + // RV32/64G listings: + // Quadrant 0 + localparam OpcodeLoad = 7'b00_000_11; + localparam OpcodeLoadFp = 7'b00_001_11; + localparam OpcodeCustom0 = 7'b00_010_11; + localparam OpcodeMiscMem = 7'b00_011_11; + localparam OpcodeOpImm = 7'b00_100_11; + localparam OpcodeAuipc = 7'b00_101_11; + localparam OpcodeOpImm32 = 7'b00_110_11; + // Quadrant 1 + localparam OpcodeStore = 7'b01_000_11; + localparam OpcodeStoreFp = 7'b01_001_11; + localparam OpcodeCustom1 = 7'b01_010_11; + localparam OpcodeAmo = 7'b01_011_11; + localparam OpcodeOp = 7'b01_100_11; + localparam OpcodeLui = 7'b01_101_11; + localparam OpcodeOp32 = 7'b01_110_11; + // Quadrant 2 + localparam OpcodeMadd = 7'b10_000_11; + localparam OpcodeMsub = 7'b10_001_11; + localparam OpcodeNmsub = 7'b10_010_11; + localparam OpcodeNmadd = 7'b10_011_11; + localparam OpcodeOpFp = 7'b10_100_11; + localparam OpcodeVec = 7'b10_101_11; + localparam OpcodeCustom2 = 7'b10_110_11; + // Quadrant 3 + localparam OpcodeBranch = 7'b11_000_11; + localparam OpcodeJalr = 7'b11_001_11; + localparam OpcodeRsrvd2 = 7'b11_010_11; + localparam OpcodeJal = 7'b11_011_11; + localparam OpcodeSystem = 7'b11_100_11; + localparam OpcodeRsrvd3 = 7'b11_101_11; + localparam OpcodeCustom3 = 7'b11_110_11; + + // RV64C/RV32C listings: + // Quadrant 0 + localparam OpcodeC0 = 2'b00; + localparam OpcodeC0Addi4spn = 3'b000; + localparam OpcodeC0Fld = 3'b001; + localparam OpcodeC0Lw = 3'b010; + localparam OpcodeC0Ld = 3'b011; + localparam OpcodeC0Zcb = 3'b100; + localparam OpcodeC0Fsd = 3'b101; + localparam OpcodeC0Sw = 3'b110; + localparam OpcodeC0Sd = 3'b111; + // Quadrant 1 + localparam OpcodeC1 = 2'b01; + localparam OpcodeC1Addi = 3'b000; + localparam OpcodeC1Addiw = 3'b001; //for RV64I only + localparam OpcodeC1Jal = 3'b001; //for RV32I only + localparam OpcodeC1Li = 3'b010; + localparam OpcodeC1LuiAddi16sp = 3'b011; + localparam OpcodeC1MiscAlu = 3'b100; + localparam OpcodeC1J = 3'b101; + localparam OpcodeC1Beqz = 3'b110; + localparam OpcodeC1Bnez = 3'b111; + // Quadrant 2 + localparam OpcodeC2 = 2'b10; + localparam OpcodeC2Slli = 3'b000; + localparam OpcodeC2Fldsp = 3'b001; + localparam OpcodeC2Lwsp = 3'b010; + localparam OpcodeC2Ldsp = 3'b011; + localparam OpcodeC2JalrMvAdd = 3'b100; + localparam OpcodeC2Fsdsp = 3'b101; + localparam OpcodeC2Swsp = 3'b110; + localparam OpcodeC2Sdsp = 3'b111; + + // ---------------------- + // Virtual Memory + // ---------------------- + // memory management, pte for sv39 + typedef struct packed { + logic [9:0] reserved; + logic [44-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; + } pte_t; + + // memory management, pte for sv32 + typedef struct packed { + logic [22-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; + } pte_sv32_t; + + // ---------------------- + // Exception Cause Codes + // ---------------------- + localparam logic [XLEN-1:0] INSTR_ADDR_MISALIGNED = 0; + localparam logic [XLEN-1:0] INSTR_ACCESS_FAULT = 1; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ILLEGAL_INSTR = 2; + localparam logic [XLEN-1:0] BREAKPOINT = 3; + localparam logic [XLEN-1:0] LD_ADDR_MISALIGNED = 4; + localparam logic [XLEN-1:0] LD_ACCESS_FAULT = 5; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ST_ADDR_MISALIGNED = 6; + localparam logic [XLEN-1:0] ST_ACCESS_FAULT = 7; // Illegal access as governed by PMPs and PMAs + localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode + localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from supervisor mode + localparam logic [XLEN-1:0] ENV_CALL_MMODE = 11; // environment call from machine mode + localparam logic [XLEN-1:0] INSTR_PAGE_FAULT = 12; // Instruction page fault + localparam logic [XLEN-1:0] LOAD_PAGE_FAULT = 13; // Load page fault + localparam logic [XLEN-1:0] STORE_PAGE_FAULT = 15; // Store page fault + localparam logic [XLEN-1:0] DEBUG_REQUEST = 24; // Debug request + + localparam int unsigned IRQ_S_SOFT = 1; + localparam int unsigned IRQ_M_SOFT = 3; + localparam int unsigned IRQ_S_TIMER = 5; + localparam int unsigned IRQ_M_TIMER = 7; + localparam int unsigned IRQ_S_EXT = 9; + localparam int unsigned IRQ_M_EXT = 11; + + localparam logic [XLEN-1:0] MIP_SSIP = 1 << IRQ_S_SOFT; + localparam logic [XLEN-1:0] MIP_MSIP = 1 << IRQ_M_SOFT; + localparam logic [XLEN-1:0] MIP_STIP = 1 << IRQ_S_TIMER; + localparam logic [XLEN-1:0] MIP_MTIP = 1 << IRQ_M_TIMER; + localparam logic [XLEN-1:0] MIP_SEIP = 1 << IRQ_S_EXT; + localparam logic [XLEN-1:0] MIP_MEIP = 1 << IRQ_M_EXT; + + localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_SOFT); + localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_SOFT); + localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_TIMER); + localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_TIMER); + localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_EXT); + localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_EXT); + + // ----- + // CSRs + // ----- + typedef enum logic [11:0] { + // Floating-Point CSRs + CSR_FFLAGS = 12'h001, + CSR_FRM = 12'h002, + CSR_FCSR = 12'h003, + CSR_FTRAN = 12'h800, + // Vector CSRs + CSR_VSTART = 12'h008, + CSR_VXSAT = 12'h009, + CSR_VXRM = 12'h00A, + CSR_VCSR = 12'h00F, + CSR_VL = 12'hC20, + CSR_VTYPE = 12'hC21, + CSR_VLENB = 12'hC22, + // Supervisor Mode CSRs + CSR_SSTATUS = 12'h100, + CSR_SIE = 12'h104, + CSR_STVEC = 12'h105, + CSR_SCOUNTEREN = 12'h106, + CSR_SSCRATCH = 12'h140, + CSR_SEPC = 12'h141, + CSR_SCAUSE = 12'h142, + CSR_STVAL = 12'h143, + CSR_SIP = 12'h144, + CSR_SATP = 12'h180, + // Machine Mode CSRs + CSR_MSTATUS = 12'h300, + CSR_MISA = 12'h301, + CSR_MEDELEG = 12'h302, + CSR_MIDELEG = 12'h303, + CSR_MIE = 12'h304, + CSR_MTVEC = 12'h305, + CSR_MCOUNTEREN = 12'h306, + CSR_MSTATUSH = 12'h310, + CSR_MCOUNTINHIBIT = 12'h320, + CSR_MHPM_EVENT_3 = 12'h323, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_4 = 12'h324, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_5 = 12'h325, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_6 = 12'h326, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_7 = 12'h327, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_8 = 12'h328, //Machine performance monitoring Event Selector + CSR_MHPM_EVENT_9 = 12'h329, //Reserved + CSR_MHPM_EVENT_10 = 12'h32A, //Reserved + CSR_MHPM_EVENT_11 = 12'h32B, //Reserved + CSR_MHPM_EVENT_12 = 12'h32C, //Reserved + CSR_MHPM_EVENT_13 = 12'h32D, //Reserved + CSR_MHPM_EVENT_14 = 12'h32E, //Reserved + CSR_MHPM_EVENT_15 = 12'h32F, //Reserved + CSR_MHPM_EVENT_16 = 12'h330, //Reserved + CSR_MHPM_EVENT_17 = 12'h331, //Reserved + CSR_MHPM_EVENT_18 = 12'h332, //Reserved + CSR_MHPM_EVENT_19 = 12'h333, //Reserved + CSR_MHPM_EVENT_20 = 12'h334, //Reserved + CSR_MHPM_EVENT_21 = 12'h335, //Reserved + CSR_MHPM_EVENT_22 = 12'h336, //Reserved + CSR_MHPM_EVENT_23 = 12'h337, //Reserved + CSR_MHPM_EVENT_24 = 12'h338, //Reserved + CSR_MHPM_EVENT_25 = 12'h339, //Reserved + CSR_MHPM_EVENT_26 = 12'h33A, //Reserved + CSR_MHPM_EVENT_27 = 12'h33B, //Reserved + CSR_MHPM_EVENT_28 = 12'h33C, //Reserved + CSR_MHPM_EVENT_29 = 12'h33D, //Reserved + CSR_MHPM_EVENT_30 = 12'h33E, //Reserved + CSR_MHPM_EVENT_31 = 12'h33F, //Reserved + CSR_MSCRATCH = 12'h340, + CSR_MEPC = 12'h341, + CSR_MCAUSE = 12'h342, + CSR_MTVAL = 12'h343, + CSR_MIP = 12'h344, + CSR_PMPCFG0 = 12'h3A0, + CSR_PMPCFG1 = 12'h3A1, + CSR_PMPCFG2 = 12'h3A2, + CSR_PMPCFG3 = 12'h3A3, + CSR_PMPADDR0 = 12'h3B0, + CSR_PMPADDR1 = 12'h3B1, + CSR_PMPADDR2 = 12'h3B2, + CSR_PMPADDR3 = 12'h3B3, + CSR_PMPADDR4 = 12'h3B4, + CSR_PMPADDR5 = 12'h3B5, + CSR_PMPADDR6 = 12'h3B6, + CSR_PMPADDR7 = 12'h3B7, + CSR_PMPADDR8 = 12'h3B8, + CSR_PMPADDR9 = 12'h3B9, + CSR_PMPADDR10 = 12'h3BA, + CSR_PMPADDR11 = 12'h3BB, + CSR_PMPADDR12 = 12'h3BC, + CSR_PMPADDR13 = 12'h3BD, + CSR_PMPADDR14 = 12'h3BE, + CSR_PMPADDR15 = 12'h3BF, + CSR_MVENDORID = 12'hF11, + CSR_MARCHID = 12'hF12, + CSR_MIMPID = 12'hF13, + CSR_MHARTID = 12'hF14, + CSR_MCONFIGPTR = 12'hF15, + CSR_MCYCLE = 12'hB00, + CSR_MCYCLEH = 12'hB80, + CSR_MINSTRET = 12'hB02, + CSR_MINSTRETH = 12'hB82, + //Performance Counters + CSR_MHPM_COUNTER_3 = 12'hB03, + CSR_MHPM_COUNTER_4 = 12'hB04, + CSR_MHPM_COUNTER_5 = 12'hB05, + CSR_MHPM_COUNTER_6 = 12'hB06, + CSR_MHPM_COUNTER_7 = 12'hB07, + CSR_MHPM_COUNTER_8 = 12'hB08, + CSR_MHPM_COUNTER_9 = 12'hB09, // reserved + CSR_MHPM_COUNTER_10 = 12'hB0A, // reserved + CSR_MHPM_COUNTER_11 = 12'hB0B, // reserved + CSR_MHPM_COUNTER_12 = 12'hB0C, // reserved + CSR_MHPM_COUNTER_13 = 12'hB0D, // reserved + CSR_MHPM_COUNTER_14 = 12'hB0E, // reserved + CSR_MHPM_COUNTER_15 = 12'hB0F, // reserved + CSR_MHPM_COUNTER_16 = 12'hB10, // reserved + CSR_MHPM_COUNTER_17 = 12'hB11, // reserved + CSR_MHPM_COUNTER_18 = 12'hB12, // reserved + CSR_MHPM_COUNTER_19 = 12'hB13, // reserved + CSR_MHPM_COUNTER_20 = 12'hB14, // reserved + CSR_MHPM_COUNTER_21 = 12'hB15, // reserved + CSR_MHPM_COUNTER_22 = 12'hB16, // reserved + CSR_MHPM_COUNTER_23 = 12'hB17, // reserved + CSR_MHPM_COUNTER_24 = 12'hB18, // reserved + CSR_MHPM_COUNTER_25 = 12'hB19, // reserved + CSR_MHPM_COUNTER_26 = 12'hB1A, // reserved + CSR_MHPM_COUNTER_27 = 12'hB1B, // reserved + CSR_MHPM_COUNTER_28 = 12'hB1C, // reserved + CSR_MHPM_COUNTER_29 = 12'hB1D, // reserved + CSR_MHPM_COUNTER_30 = 12'hB1E, // reserved + CSR_MHPM_COUNTER_31 = 12'hB1F, // reserved + CSR_MHPM_COUNTER_3H = 12'hB83, + CSR_MHPM_COUNTER_4H = 12'hB84, + CSR_MHPM_COUNTER_5H = 12'hB85, + CSR_MHPM_COUNTER_6H = 12'hB86, + CSR_MHPM_COUNTER_7H = 12'hB87, + CSR_MHPM_COUNTER_8H = 12'hB88, + CSR_MHPM_COUNTER_9H = 12'hB89, // reserved + CSR_MHPM_COUNTER_10H = 12'hB8A, // reserved + CSR_MHPM_COUNTER_11H = 12'hB8B, // reserved + CSR_MHPM_COUNTER_12H = 12'hB8C, // reserved + CSR_MHPM_COUNTER_13H = 12'hB8D, // reserved + CSR_MHPM_COUNTER_14H = 12'hB8E, // reserved + CSR_MHPM_COUNTER_15H = 12'hB8F, // reserved + CSR_MHPM_COUNTER_16H = 12'hB90, // reserved + CSR_MHPM_COUNTER_17H = 12'hB91, // reserved + CSR_MHPM_COUNTER_18H = 12'hB92, // reserved + CSR_MHPM_COUNTER_19H = 12'hB93, // reserved + CSR_MHPM_COUNTER_20H = 12'hB94, // reserved + CSR_MHPM_COUNTER_21H = 12'hB95, // reserved + CSR_MHPM_COUNTER_22H = 12'hB96, // reserved + CSR_MHPM_COUNTER_23H = 12'hB97, // reserved + CSR_MHPM_COUNTER_24H = 12'hB98, // reserved + CSR_MHPM_COUNTER_25H = 12'hB99, // reserved + CSR_MHPM_COUNTER_26H = 12'hB9A, // reserved + CSR_MHPM_COUNTER_27H = 12'hB9B, // reserved + CSR_MHPM_COUNTER_28H = 12'hB9C, // reserved + CSR_MHPM_COUNTER_29H = 12'hB9D, // reserved + CSR_MHPM_COUNTER_30H = 12'hB9E, // reserved + CSR_MHPM_COUNTER_31H = 12'hB9F, // reserved + // Cache Control (platform specifc) + CSR_DCACHE = 12'h7C1, + CSR_ICACHE = 12'h7C0, + // Accelerator memory consistency (platform specific) + CSR_ACC_CONS = 12'h7C2, + // Triggers + CSR_TSELECT = 12'h7A0, + CSR_TDATA1 = 12'h7A1, + CSR_TDATA2 = 12'h7A2, + CSR_TDATA3 = 12'h7A3, + CSR_TINFO = 12'h7A4, + // Debug CSR + CSR_DCSR = 12'h7b0, + CSR_DPC = 12'h7b1, + CSR_DSCRATCH0 = 12'h7b2, // optional + CSR_DSCRATCH1 = 12'h7b3, // optional + // Counters and Timers (User Mode - R/O Shadows) + CSR_CYCLE = 12'hC00, + CSR_CYCLEH = 12'hC80, + CSR_TIME = 12'hC01, + CSR_TIMEH = 12'hC81, + CSR_INSTRET = 12'hC02, + CSR_INSTRETH = 12'hC82, + // Performance counters (User Mode - R/O Shadows) + CSR_HPM_COUNTER_3 = 12'hC03, + CSR_HPM_COUNTER_4 = 12'hC04, + CSR_HPM_COUNTER_5 = 12'hC05, + CSR_HPM_COUNTER_6 = 12'hC06, + CSR_HPM_COUNTER_7 = 12'hC07, + CSR_HPM_COUNTER_8 = 12'hC08, + CSR_HPM_COUNTER_9 = 12'hC09, // reserved + CSR_HPM_COUNTER_10 = 12'hC0A, // reserved + CSR_HPM_COUNTER_11 = 12'hC0B, // reserved + CSR_HPM_COUNTER_12 = 12'hC0C, // reserved + CSR_HPM_COUNTER_13 = 12'hC0D, // reserved + CSR_HPM_COUNTER_14 = 12'hC0E, // reserved + CSR_HPM_COUNTER_15 = 12'hC0F, // reserved + CSR_HPM_COUNTER_16 = 12'hC10, // reserved + CSR_HPM_COUNTER_17 = 12'hC11, // reserved + CSR_HPM_COUNTER_18 = 12'hC12, // reserved + CSR_HPM_COUNTER_19 = 12'hC13, // reserved + CSR_HPM_COUNTER_20 = 12'hC14, // reserved + CSR_HPM_COUNTER_21 = 12'hC15, // reserved + CSR_HPM_COUNTER_22 = 12'hC16, // reserved + CSR_HPM_COUNTER_23 = 12'hC17, // reserved + CSR_HPM_COUNTER_24 = 12'hC18, // reserved + CSR_HPM_COUNTER_25 = 12'hC19, // reserved + CSR_HPM_COUNTER_26 = 12'hC1A, // reserved + CSR_HPM_COUNTER_27 = 12'hC1B, // reserved + CSR_HPM_COUNTER_28 = 12'hC1C, // reserved + CSR_HPM_COUNTER_29 = 12'hC1D, // reserved + CSR_HPM_COUNTER_30 = 12'hC1E, // reserved + CSR_HPM_COUNTER_31 = 12'hC1F, // reserved + CSR_HPM_COUNTER_3H = 12'hC83, + CSR_HPM_COUNTER_4H = 12'hC84, + CSR_HPM_COUNTER_5H = 12'hC85, + CSR_HPM_COUNTER_6H = 12'hC86, + CSR_HPM_COUNTER_7H = 12'hC87, + CSR_HPM_COUNTER_8H = 12'hC88, + CSR_HPM_COUNTER_9H = 12'hC89, // reserved + CSR_HPM_COUNTER_10H = 12'hC8A, // reserved + CSR_HPM_COUNTER_11H = 12'hC8B, // reserved + CSR_HPM_COUNTER_12H = 12'hC8C, // reserved + CSR_HPM_COUNTER_13H = 12'hC8D, // reserved + CSR_HPM_COUNTER_14H = 12'hC8E, // reserved + CSR_HPM_COUNTER_15H = 12'hC8F, // reserved + CSR_HPM_COUNTER_16H = 12'hC90, // reserved + CSR_HPM_COUNTER_17H = 12'hC91, // reserved + CSR_HPM_COUNTER_18H = 12'hC92, // reserved + CSR_HPM_COUNTER_19H = 12'hC93, // reserved + CSR_HPM_COUNTER_20H = 12'hC94, // reserved + CSR_HPM_COUNTER_21H = 12'hC95, // reserved + CSR_HPM_COUNTER_22H = 12'hC96, // reserved + CSR_HPM_COUNTER_23H = 12'hC97, // reserved + CSR_HPM_COUNTER_24H = 12'hC98, // reserved + CSR_HPM_COUNTER_25H = 12'hC99, // reserved + CSR_HPM_COUNTER_26H = 12'hC9A, // reserved + CSR_HPM_COUNTER_27H = 12'hC9B, // reserved + CSR_HPM_COUNTER_28H = 12'hC9C, // reserved + CSR_HPM_COUNTER_29H = 12'hC9D, // reserved + CSR_HPM_COUNTER_30H = 12'hC9E, // reserved + CSR_HPM_COUNTER_31H = 12'hC9F // reserved + } csr_reg_t; + + localparam logic [63:0] SSTATUS_UIE = 'h00000001; + localparam logic [63:0] SSTATUS_SIE = 'h00000002; + localparam logic [63:0] SSTATUS_SPIE = 'h00000020; + localparam logic [63:0] SSTATUS_SPP = 'h00000100; + localparam logic [63:0] SSTATUS_FS = 'h00006000; + localparam logic [63:0] SSTATUS_XS = 'h00018000; + localparam logic [63:0] SSTATUS_SUM = 'h00040000; + localparam logic [63:0] SSTATUS_MXR = 'h00080000; + localparam logic [63:0] SSTATUS_UPIE = 'h00000010; + localparam logic [63:0] SSTATUS_UXL = 64'h0000000300000000; + localparam logic [63:0] SSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; + + localparam logic [63:0] MSTATUS_UIE = 'h00000001; + localparam logic [63:0] MSTATUS_SIE = 'h00000002; + localparam logic [63:0] MSTATUS_HIE = 'h00000004; + localparam logic [63:0] MSTATUS_MIE = 'h00000008; + localparam logic [63:0] MSTATUS_UPIE = 'h00000010; + localparam logic [63:0] MSTATUS_SPIE = 'h00000020; + localparam logic [63:0] MSTATUS_HPIE = 'h00000040; + localparam logic [63:0] MSTATUS_MPIE = 'h00000080; + localparam logic [63:0] MSTATUS_SPP = 'h00000100; + localparam logic [63:0] MSTATUS_HPP = 'h00000600; + localparam logic [63:0] MSTATUS_MPP = 'h00001800; + localparam logic [63:0] MSTATUS_FS = 'h00006000; + localparam logic [63:0] MSTATUS_XS = 'h00018000; + localparam logic [63:0] MSTATUS_MPRV = 'h00020000; + localparam logic [63:0] MSTATUS_SUM = 'h00040000; + localparam logic [63:0] MSTATUS_MXR = 'h00080000; + localparam logic [63:0] MSTATUS_TVM = 'h00100000; + localparam logic [63:0] MSTATUS_TW = 'h00200000; + localparam logic [63:0] MSTATUS_TSR = 'h00400000; + localparam logic [63:0] MSTATUS_UXL = {30'h0000000, IS_XLEN64, IS_XLEN64, 32'h00000000}; + localparam logic [63:0] MSTATUS_SXL = {28'h0000000, IS_XLEN64, IS_XLEN64, 34'h00000000}; + localparam logic [63:0] MSTATUS_SD = {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000}; + + typedef enum logic [2:0] { + CSRRW = 3'h1, + CSRRS = 3'h2, + CSRRC = 3'h3, + CSRRWI = 3'h5, + CSRRSI = 3'h6, + CSRRCI = 3'h7 + } csr_op_t; + + // decoded CSR address + typedef struct packed { + logic [1:0] rw; + priv_lvl_t priv_lvl; + logic [7:0] address; + } csr_addr_t; + + typedef union packed { + csr_reg_t address; + csr_addr_t csr_decode; + } csr_t; + + // Floating-Point control and status register (32-bit!) + typedef struct packed { + logic [31:15] reserved; // reserved for L extension, return 0 otherwise + logic [6:0] fprec; // div/sqrt precision control + logic [2:0] frm; // float rounding mode + logic [4:0] fflags; // float exception flags + } fcsr_t; + + // PMP + typedef enum logic [1:0] { + OFF = 2'b00, + TOR = 2'b01, + NA4 = 2'b10, + NAPOT = 2'b11 + } pmp_addr_mode_t; + + // PMP Access Type + typedef enum logic [2:0] { + ACCESS_NONE = 3'b000, + ACCESS_READ = 3'b001, + ACCESS_WRITE = 3'b010, + ACCESS_EXEC = 3'b100 + } pmp_access_t; + + typedef struct packed { + logic x; + logic w; + logic r; + } pmpcfg_access_t; + + // packed struct of a PMP configuration register (8bit) + typedef struct packed { + logic locked; // lock this configuration + logic [1:0] reserved; + pmp_addr_mode_t addr_mode; // Off, TOR, NA4, NAPOT + pmpcfg_access_t access_type; + } pmpcfg_t; + + // ----- + // Debug + // ----- + typedef struct packed { + logic [31:28] xdebugver; + logic [27:16] zero2; + logic ebreakm; + logic zero1; + logic ebreaks; + logic ebreaku; + logic stepie; + logic stopcount; + logic stoptime; + logic [8:6] cause; + logic zero0; + logic mprven; + logic nmip; + logic step; + priv_lvl_t prv; + } dcsr_t; + + // Instruction Generation *incomplete* + function automatic logic [31:0] jal(logic [4:0] rd, logic [20:0] imm); + // OpCode Jal + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; + endfunction + + function automatic logic [31:0] jalr(logic [4:0] rd, logic [4:0] rs1, logic [11:0] offset); + // OpCode Jal + return {offset[11:0], rs1, 3'b0, rd, 7'h67}; + endfunction + + function automatic logic [31:0] andi(logic [4:0] rd, logic [4:0] rs1, logic [11:0] imm); + // OpCode andi + return {imm[11:0], rs1, 3'h7, rd, 7'h13}; + endfunction + + function automatic logic [31:0] slli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt); + // OpCode slli + return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; + endfunction + + function automatic logic [31:0] srli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt); + // OpCode srli + return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; + endfunction + + function automatic logic [31:0] load(logic [2:0] size, logic [4:0] dest, logic [4:0] base, + logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'h03}; + endfunction + + function automatic logic [31:0] auipc(logic [4:0] rd, logic [20:0] imm); + // OpCode Auipc + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; + endfunction + + function automatic logic [31:0] store(logic [2:0] size, logic [4:0] src, logic [4:0] base, + logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'h23}; + endfunction + + function automatic logic [31:0] float_load(logic [2:0] size, logic [4:0] dest, logic [4:0] base, + logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'b00_001_11}; + endfunction + + function automatic logic [31:0] float_store(logic [2:0] size, logic [4:0] src, logic [4:0] base, + logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; + endfunction + + function automatic logic [31:0] csrw(csr_reg_t csr, logic [4:0] rs1); + // CSRRW, rd, OpCode System + return {csr, rs1, 3'h1, 5'h0, 7'h73}; + endfunction + + function automatic logic [31:0] csrr(csr_reg_t csr, logic [4:0] dest); + // rs1, CSRRS, rd, OpCode System + return {csr, 5'h0, 3'h2, dest, 7'h73}; + endfunction + + function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, + logic [11:0] offset); + // OpCode Branch + return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; + endfunction + + function automatic logic [31:0] ebreak(); + return 32'h00100073; + endfunction + + function automatic logic [31:0] wfi(); + return 32'h10500073; + endfunction + + function automatic logic [31:0] nop(); + return 32'h00000013; + endfunction + + function automatic logic [31:0] illegal(); + return 32'h00000000; + endfunction + + + // trace log compatible to spikes commit log feature + // pragma translate_off + function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr, + logic [4:0] rd, logic [63:0] result, logic rd_fpr); + string rd_s; + string instr_word; + + automatic string rf_s = rd_fpr ? "f" : "x"; + + if (instr[1:0] != 2'b11) begin + instr_word = $sformatf("(0x%h)", instr[15:0]); + end else begin + instr_word = $sformatf("(0x%h)", instr); + end + + if (rd < 10) rd_s = $sformatf("%s %0d", rf_s, rd); + else rd_s = $sformatf("%s%0d", rf_s, rd); + + if (rd_fpr || rd != 0) begin + // 0 0x0000000080000118 (0xeecf8f93) x31 0x0000000080004000 + return $sformatf("%d 0x%h %s %s 0x%h\n", priv_lvl, pc, instr_word, rd_s, result); + end else begin + // 0 0x000000008000019c (0x0040006f) + return $sformatf("%d 0x%h %s\n", priv_lvl, pc, instr_word); + end + endfunction + + typedef struct { + byte priv; + longint unsigned pc; + byte is_fp; + byte rd; + longint unsigned data; + int unsigned instr; + byte was_exception; + } commit_log_t; + // pragma translate_on endpackage From 7f180fc238b02bccded08ca7614d46d0f279c610 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 21 Nov 2023 21:33:52 +0100 Subject: [PATCH 05/64] modify data structures for mmu --- core/include/ariane_pkg.sv | 13 +++++++++++++ core/include/riscv_pkg.sv | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index 446f609a61..e822bd3738 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -35,6 +35,9 @@ package ariane_pkg; ); // depending on the number of scoreboard entries we need that many bits // to uniquely identify the entry in the scoreboard localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; + localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; + localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; + localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; localparam BITS_SATURATION_COUNTER = 2; localparam ISSUE_WIDTH = 1; @@ -767,6 +770,16 @@ package ariane_pkg; riscv::pte_sv32_t content; } tlb_update_sv32_t; + typedef struct packed { + logic valid; // valid flag + logic is_2M; // + logic is_4M; // + logic is_1G; // + logic [VPN_LEN-1:0] vpn; // + logic [ASID_LEN-1:0] asid; // + riscv::pte_cva6_t content; +} tlb_update_cva6_t; + typedef enum logic [1:0] { FE_NONE, FE_INSTR_ACCESS_FAULT, diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 2a9d919c1a..317bf2ac4d 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -321,6 +321,20 @@ package riscv; logic v; } pte_sv32_t; + // memory management, pte for cva6 + typedef struct packed { + logic [PPNW-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; +} pte_cva6_t; + // ---------------------- // Exception Cause Codes // ---------------------- From 1d2b65f41a8e6a7c1f6f1c52427f6fe9176b8716 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 22 Nov 2023 14:31:38 +0100 Subject: [PATCH 06/64] created first version of common shared tlb --- core/include/ariane_pkg.sv | 4 +- core/mmu_unify/cva6_mmu.sv | 576 ++++++++++++++++++++++++++++++ core/mmu_unify/cva6_shared_tlb.sv | 393 ++++++++++++++++++++ core/mmu_unify/cva6_tlb.sv | 2 +- 4 files changed, 971 insertions(+), 4 deletions(-) create mode 100644 core/mmu_unify/cva6_mmu.sv create mode 100644 core/mmu_unify/cva6_shared_tlb.sv diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index e822bd3738..cc5705cdc7 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -772,9 +772,7 @@ package ariane_pkg; typedef struct packed { logic valid; // valid flag - logic is_2M; // - logic is_4M; // - logic is_1G; // + logic [PT_LEVELS-2:0] is_page; // logic [VPN_LEN-1:0] vpn; // logic [ASID_LEN-1:0] asid; // riscv::pte_cva6_t content; diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv new file mode 100644 index 0000000000..5c38ad9949 --- /dev/null +++ b/core/mmu_unify/cva6_mmu.sv @@ -0,0 +1,576 @@ +// Copyright (c) 2021 Thales. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Sebastien Jacq Thales Research & Technology +// Date: 17/07/2021 +// +// Additional contributions by: +// Sebastien Jacq - sjthales on github.com +// +// Description: Memory Management Unit for CV32A6, contains TLB and +// address translation unit. Sv32 as defined in RISC-V +// privilege specification 1.11-WIP. +// This module is an adaptation of the MMU Sv39 developed +// by Florian Zaruba to the Sv32 standard. +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 +// =========================================================================== // + +module cva6_mmu +import ariane_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 +) ( + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i +); + +logic iaccess_err; // insufficient privilege to access this instruction page +logic daccess_err; // insufficient privilege to access this data page +logic ptw_active; // PTW is currently walking a page table +logic walking_instr; // PTW is walking because of an ITLB miss +logic ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + +logic [riscv::VLEN-1:0] update_vaddr; +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; +tlb_update_sv32_t update_shared_tlb_sv32; + +logic itlb_lu_access; +riscv::pte_cva6_t itlb_content; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; + +logic dtlb_lu_access; +riscv::pte_cva6_t dtlb_content; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic dtlb_lu_hit; + +logic shared_tlb_access; +logic [riscv::VLEN-1:0] shared_tlb_vaddr; +logic shared_tlb_hit; + +logic itlb_req; + + +// Assignments +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + +cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH) +) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) +); + +cva6_ptw_sv32 #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH) +) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb_sv32), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + +); + +assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; +assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; +assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; +assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; +assign update_shared_tlb_sv32.content = update_shared_tlb.content; + +// ila_1 i_ila_1 ( +// .clk(clk_i), // input wire clk +// .probe0({req_port_o.address_tag, req_port_o.address_index}), +// .probe1(req_port_o.data_req), // input wire [63:0] probe1 +// .probe2(req_port_i.data_gnt), // input wire [0:0] probe2 +// .probe3(req_port_i.data_rdata), // input wire [0:0] probe3 +// .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 +// .probe5(ptw_error), // input wire [1:0] probe5 +// .probe6(update_vaddr), // input wire [0:0] probe6 +// .probe7(update_itlb.valid), // input wire [0:0] probe7 +// .probe8(update_dtlb.valid), // input wire [0:0] probe8 +// .probe9(dtlb_lu_access), // input wire [0:0] probe9 +// .probe10(lsu_vaddr_i), // input wire [0:0] probe10 +// .probe11(dtlb_lu_hit), // input wire [0:0] probe11 +// .probe12(itlb_lu_access), // input wire [0:0] probe12 +// .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13 +// .probe14(itlb_lu_hit) // input wire [0:0] probe13 +// ); + +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + if (riscv::PLEN > riscv::VLEN) + icache_areq_o.fetch_paddr = { + {riscv::PLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr + }; // play through in case we disabled address translation + else + icache_areq_o.fetch_paddr = {2'b00, icache_areq_i.fetch_vaddr[riscv::VLEN-1:0]};// play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end + + icache_areq_o.fetch_valid = 1'b0; + + // 4K page + icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // Mega page + if (itlb_is_4M) begin + icache_areq_o.fetch_paddr[21:12] = icache_areq_i.fetch_vaddr[21:12]; + end + + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; //to check on wave --> not connected + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1 + }; //to check on wave --> not connected + end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; //to check on wave + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1 + }; //to check on wave --> not connected + end + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1 + }; //to check on wave --> not connected + end +end + +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); + +// Instruction fetch +pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_sv32_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic dtlb_is_4M_n, dtlb_is_4M_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_4M_n = dtlb_is_4M; + + if (riscv::PLEN > riscv::VLEN) begin + lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q}; + lsu_dtlb_ppn_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n[riscv::VLEN-1:12]}; + end else begin + lsu_paddr_o = {2'b00, lsu_vaddr_q[riscv::VLEN-1:0]}; + lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PPNW-1:0]; + end + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + // 4K page + lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; + lsu_dtlb_ppn_o = dtlb_content.ppn; + // Mega page + if (dtlb_is_4M_q) begin + lsu_paddr_o[21:12] = lsu_vaddr_q[21:12]; + lsu_dtlb_ppn_o[21:12] = lsu_vaddr_n[21:12]; + end + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; //to check on wave + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1}; + end + end + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; + end + end +end + +// Load/store PMP check +pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_4M_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_4M_q <= dtlb_is_4M_n; + end +end +endmodule diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv new file mode 100644 index 0000000000..86673fb5cb --- /dev/null +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -0,0 +1,393 @@ +// Copyright (c) 2023 Thales. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Sebastien Jacq - Thales Research & Technology +// Date: 08/03/2023 +// +// Description: N-way associative shared TLB, it allows to reduce the number +// of ITLB and DTLB entries. +// + +/* verilator lint_off WIDTH */ + +module cva6_shared_tlb +import ariane_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int SHARED_TLB_DEPTH = 64, + parameter int SHARED_TLB_WAYS = 2, + parameter int ASID_WIDTH = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, + + input logic enable_translation_i, // CSRs indicate to enable SV32 + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + + input logic [ASID_WIDTH-1:0] asid_i, + + // from TLBs + // did we miss? + input logic itlb_access_i, + input logic itlb_hit_i, + input logic [riscv::VLEN-1:0] itlb_vaddr_i, + + input logic dtlb_access_i, + input logic dtlb_hit_i, + input logic [riscv::VLEN-1:0] dtlb_vaddr_i, + + // to TLBs, update logic + output tlb_update_cva6_t itlb_update_o, + output tlb_update_cva6_t dtlb_update_o, + + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + + output logic shared_tlb_access_o, + output logic shared_tlb_hit_o, + output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, + + output logic itlb_req_o, + + // Update shared TLB in case of miss + input tlb_update_cva6_t shared_tlb_update_i + +); + +function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(SHARED_TLB_WAYS +)-1:0] in); + logic [SHARED_TLB_WAYS-1:0] out; + out = '0; + out[in] = 1'b1; + return out; +endfunction + +typedef struct packed { + logic [ASID_LEN-1:0] asid; // + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; +} shared_tag_t; + +shared_tag_t shared_tag_wr; +shared_tag_t [SHARED_TLB_WAYS-1:0] shared_tag_rd; + +logic [SHARED_TLB_DEPTH-1:0][SHARED_TLB_WAYS-1:0] shared_tag_valid_q, shared_tag_valid_d; + +logic [ SHARED_TLB_WAYS-1:0] shared_tag_valid; + +logic [ SHARED_TLB_WAYS-1:0] tag_wr_en; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_wr_addr; +logic [ $bits(shared_tag_t)-1:0] tag_wr_data; + +logic [ SHARED_TLB_WAYS-1:0] tag_rd_en; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_rd_addr; +logic [ $bits(shared_tag_t)-1:0] tag_rd_data [SHARED_TLB_WAYS-1:0]; + +logic [ SHARED_TLB_WAYS-1:0] tag_req; +logic [ SHARED_TLB_WAYS-1:0] tag_we; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_addr; + +logic [ SHARED_TLB_WAYS-1:0] pte_wr_en; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr; +logic [$bits(riscv::pte_cva6_t)-1:0] pte_wr_data; + +logic [ SHARED_TLB_WAYS-1:0] pte_rd_en; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr; +logic [$bits(riscv::pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; + +logic [ SHARED_TLB_WAYS-1:0] pte_req; +logic [ SHARED_TLB_WAYS-1:0] pte_we; +logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_addr; + +logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn_d,vpn_q; +logic [PT_LEVELS-1:0] vpn_match; +logic [PT_LEVELS-1:0] page_match; +logic [PT_LEVELS-1:0] level_match; + +riscv::pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; + +logic [riscv::VLEN-1-12:0] itlb_vpn_q; +logic [riscv::VLEN-1-12:0] dtlb_vpn_q; + +logic [ASID_WIDTH-1:0] tlb_update_asid_q, tlb_update_asid_d; + +logic shared_tlb_access_q, shared_tlb_access_d; +logic shared_tlb_hit_d; +logic [riscv::VLEN-1:0] shared_tlb_vaddr_q, shared_tlb_vaddr_d; + +logic itlb_req_d, itlb_req_q; +logic dtlb_req_d, dtlb_req_q; + +// replacement strategy +logic [SHARED_TLB_WAYS-1:0] way_valid; +logic update_lfsr; // shift the LFSR +logic [$clog2(SHARED_TLB_WAYS)-1:0] inv_way; // first non-valid encountered +logic [$clog2(SHARED_TLB_WAYS)-1:0] rnd_way; // random index for replacement +logic [$clog2(SHARED_TLB_WAYS)-1:0] repl_way; // way to replace +logic [SHARED_TLB_WAYS-1:0] repl_way_oh_d; // way to replace (onehot) +logic all_ways_valid; // we need to switch repl strategy since all are valid + +assign shared_tlb_access_o = shared_tlb_access_q; +assign shared_tlb_hit_o = shared_tlb_hit_d; +assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; + +assign itlb_req_o = itlb_req_q; + +/////////////////////////////////////////////////////// +// tag comparison, hit generation +/////////////////////////////////////////////////////// +always_comb begin : itlb_dtlb_miss + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; + vpn_d = vpn_q; + + tag_rd_en = '0; + pte_rd_en = '0; + + itlb_req_d = 1'b0; + dtlb_req_d = 1'b0; + + tlb_update_asid_d = tlb_update_asid_q; + + shared_tlb_access_d = '0; + shared_tlb_vaddr_d = shared_tlb_vaddr_q; + + tag_rd_addr = '0; + pte_rd_addr = '0; + + // if we got an ITLB miss + if (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin + tag_rd_en = '1; + tag_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + pte_rd_en = '1; + pte_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + + for (int unsigned x = 0; x < PT_LEVELS; x++) begin + vpn[x] = itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; + end + + itlb_miss_o = 1'b1; + itlb_req_d = 1'b1; + + tlb_update_asid_d = asid_i; + + shared_tlb_access_d = 1'b1; + shared_tlb_vaddr_d = itlb_vaddr_i; + + // we got an DTLB miss + end else if (en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) begin + tag_rd_en = '1; + tag_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + pte_rd_en = '1; + pte_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + + for (int unsigned x = 0; x < PT_LEVELS; x++) begin + vpn[x] = itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; + end + + dtlb_miss_o = 1'b1; + dtlb_req_d = 1'b1; + + tlb_update_asid_d = asid_i; + + shared_tlb_access_d = 1'b1; + shared_tlb_vaddr_d = dtlb_vaddr_i; + end +end //itlb_dtlb_miss + +always_comb begin : tag_comparison + shared_tlb_hit_d = 1'b0; + dtlb_update_o = '0; + itlb_update_o = '0; + + //at level 0 make page match always 1 + page_match = (shared_tag_rd[i].is_page)*2 +1; + + //build level match vector according to vpn_match and page_match + //a level has a match if all vpn of higher levels and current have a match, + //AND the page_match is also set + //At level 0 the page match is also set, so this level will have a match + //if all vpn levels match + for (int unsigned x = 0; x < PT_LEVELS; x++) begin + vpn_match[x] = vpn[x] == shared_tag_rd[i].vpn[x]; + level_match[x] = &vpn_match[PT_LEVELS-1:x] & page_match[x]; + end + //number of ways + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + if (shared_tag_valid[i] && ((tlb_update_asid_q == shared_tag_rd[i].asid) || pte[i].g)) begin + for (int unsigned a = PT_LEVELS-1; a>=0;a--) begin + //find highest level to have a match and break loop + if (level_match[a]) begin + + shared_tlb_hit_d = 1'b1; + if (itlb_req_q) begin + itlb_update_o.valid = 1'b1; + itlb_update_o.vpn = itlb_vpn_q; + itlb_update_o.is_page = shared_tag_rd[i].is_page; + itlb_update_o.asid = tlb_update_asid_q; + itlb_update_o.content = pte[i]; + end else if (dtlb_req_q) begin + dtlb_update_o.valid = 1'b1; + dtlb_update_o.vpn = dtlb_vpn_q; + dtlb_update_o.is_page = shared_tag_rd[i].is_page; + dtlb_update_o.asid = tlb_update_asid_q; + dtlb_update_o.content = pte[i]; + end + end + end + end + end +end //tag_comparison + +// sequential process +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + itlb_vpn_q <= '0; + dtlb_vpn_q <= '0; + tlb_update_asid_q <= '0; + shared_tlb_access_q <= '0; + shared_tlb_vaddr_q <= '0; + shared_tag_valid_q <= '0; + vpn_q <= 0; + itlb_req_q <= '0; + dtlb_req_q <= '0; + shared_tag_valid <= '0; + end else begin + itlb_vpn_q <= itlb_vaddr_i[riscv::SV-1:12]; + dtlb_vpn_q <= dtlb_vaddr_i[riscv::SV-1:12]; + tlb_update_asid_q <= tlb_update_asid_d; + shared_tlb_access_q <= shared_tlb_access_d; + shared_tlb_vaddr_q <= shared_tlb_vaddr_d; + shared_tag_valid_q <= shared_tag_valid_d; + vpn_q <= vpn_d; + itlb_req_q <= itlb_req_d; + dtlb_req_q <= dtlb_req_d; + shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; + end +end + +// ------------------ +// Update and Flush +// ------------------ +always_comb begin : update_flush + shared_tag_valid_d = shared_tag_valid_q; + tag_wr_en = '0; + pte_wr_en = '0; + + if (flush_i) begin + shared_tag_valid_d = '0; + end else if (shared_tlb_update_i.valid) begin + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + if (repl_way_oh_d[i]) begin + shared_tag_valid_d[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]][i] = 1'b1; + tag_wr_en[i] = 1'b1; + pte_wr_en[i] = 1'b1; + end + end + end +end //update_flush + +assign shared_tag_wr.asid = shared_tlb_update_i.asid; + +genvar i; + generate + for (i=0; i < PT_LEVELS; i++) begin + assign shared_tag_wr.vpn = shared_tlb_update_i.vpn[1((VPN_LEN/PT_LEVELS)*(x+1))-1:((VPN_LEN/PT_LEVELS)*x)]; + end + endgenerate + +assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; + +assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; +assign tag_wr_data = shared_tag_wr; + +assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; +assign pte_wr_data = shared_tlb_update_i.content; + +assign way_valid = shared_tag_valid_q[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]]; +assign repl_way = (all_ways_valid) ? rnd_way : inv_way; +assign update_lfsr = shared_tlb_update_i.valid & all_ways_valid; +assign repl_way_oh_d = (shared_tlb_update_i.valid) ? shared_tlb_way_bin2oh(repl_way) : '0; + +lzc #( + .WIDTH(SHARED_TLB_WAYS) +) i_lzc ( + .in_i (~way_valid), + .cnt_o (inv_way), + .empty_o(all_ways_valid) +); + +lfsr #( + .LfsrWidth(8), + .OutWidth ($clog2(SHARED_TLB_WAYS)) +) i_lfsr ( + .clk_i (clk_i), + .rst_ni(rst_ni), + .en_i (update_lfsr), + .out_o (rnd_way) +); + +/////////////////////////////////////////////////////// +// memory arrays and regs +/////////////////////////////////////////////////////// + +assign tag_req = tag_wr_en | tag_rd_en; +assign tag_we = tag_wr_en; +assign tag_addr = tag_wr_en ? tag_wr_addr : tag_rd_addr; + +assign pte_req = pte_wr_en | pte_rd_en; +assign pte_we = pte_wr_en; +assign pte_addr = pte_wr_en ? pte_wr_addr : pte_rd_addr; + +for (genvar i = 0; i < SHARED_TLB_WAYS; i++) begin : gen_sram + // Tag RAM + sram #( + .DATA_WIDTH($bits(shared_tag_t)), + .NUM_WORDS (SHARED_TLB_DEPTH) + ) tag_sram ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (tag_req[i]), + .we_i (tag_we[i]), + .addr_i (tag_addr), + .wuser_i('0), + .wdata_i(tag_wr_data), + .be_i ('1), + .ruser_o(), + .rdata_o(tag_rd_data[i]) + ); + + assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]); + + // PTE RAM + sram #( + .DATA_WIDTH($bits(riscv::pte_sv32_t)), + .NUM_WORDS (SHARED_TLB_DEPTH) + ) pte_sram ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (pte_req[i]), + .we_i (pte_we[i]), + .addr_i (pte_addr), + .wuser_i('0), + .wdata_i(pte_wr_data), + .be_i ('1), + .ruser_o(), + .rdata_o(pte_rd_data[i]) + ); + assign pte[i] = riscv::pte_cva6_t'(pte_rd_data[i]); +end +endmodule + +/* verilator lint_on WIDTH */ diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e0cab24fb1..2c0d1c993d 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -91,7 +91,7 @@ always_comb begin : translation //At level 0 the page match is also set, so this level will have a match //if all vpn levels match for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vpn_match[x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; == tags_q[i].vpn[x]; + vpn_match[x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; level_match[x] = &vpn_match[PT_LEVELS-1:x] & page_match[x]; end From 3973054053236b98c9793b23a88ce35c5810beac Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 24 Nov 2023 09:59:07 +0100 Subject: [PATCH 07/64] cva6_tlb unified : dv-riscv-mmu-sv32-test.sh passes correctly --- Bender.yml | 12 ++-- ariane.core | 2 + core/Flist.cva6 | 5 ++ core/load_store_unit.sv | 6 +- core/mmu_unify/cva6_mmu.sv | 36 +++++++---- core/mmu_unify/cva6_tlb.sv | 118 ++++++++++++++++--------------------- src_files.yml | 2 + 7 files changed, 95 insertions(+), 86 deletions(-) diff --git a/Bender.yml b/Bender.yml index c21ec54dc8..98e9e7854c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -48,8 +48,8 @@ sources: - core/include/cv32a6_imac_sv0_config_pkg.sv - core/include/riscv_pkg.sv - core/include/ariane_pkg.sv - - core/mmu_sv32/cva6_tlb_sv32.sv - - core/mmu_sv32/cva6_mmu_sv32.sv + - core/mmu_unify/cva6_tlb.sv + - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv - core/cva6_accel_first_pass_decoder_stub.sv @@ -58,8 +58,8 @@ sources: - core/include/cv32a6_imac_sv32_config_pkg.sv - core/include/riscv_pkg.sv - core/include/ariane_pkg.sv - - core/mmu_sv32/cva6_tlb_sv32.sv - - core/mmu_sv32/cva6_mmu_sv32.sv + - core/mmu_unify/cva6_tlb.sv + - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv - core/cva6_accel_first_pass_decoder_stub.sv @@ -68,8 +68,8 @@ sources: - core/include/cv32a6_imafc_sv32_config_pkg.sv - core/include/riscv_pkg.sv - core/include/ariane_pkg.sv - - core/mmu_sv32/cva6_tlb_sv32.sv - - core/mmu_sv32/cva6_mmu_sv32.sv + - core/mmu_unify/cva6_tlb.sv + - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv - core/cva6_accel_first_pass_decoder_stub.sv diff --git a/ariane.core b/ariane.core index 501f296c14..f189cb7ace 100644 --- a/ariane.core +++ b/ariane.core @@ -35,6 +35,7 @@ filesets: - src/miss_handler.sv - src/mmu_sv39/mmu.sv - src/mmu_sv32/cva6_mmu_sv32.sv + - src/mmu_unify/cva6_mmu.sv - src/mult.sv - src/nbdcache.sv - src/pcgen_stage.sv @@ -47,6 +48,7 @@ filesets: - src/store_unit.sv - src/mmu_sv39/tlb.sv - src/mmu_sv32/cva6_tlb_sv32.sv + - src/mmu_unify/cva6_tlb.sv file_type : systemVerilogSource depend : - pulp-platform.org::axi_mem_if diff --git a/core/Flist.cva6 b/core/Flist.cva6 index 46a541603e..6ec104b08c 100644 --- a/core/Flist.cva6 +++ b/core/Flist.cva6 @@ -187,4 +187,9 @@ ${CVA6_REPO_DIR}/core/mmu_sv32/cva6_ptw_sv32.sv ${CVA6_REPO_DIR}/core/mmu_sv32/cva6_tlb_sv32.sv ${CVA6_REPO_DIR}/core/mmu_sv32/cva6_shared_tlb_sv32.sv +// MMU Unify +${CVA6_REPO_DIR}/core/mmu_unify/cva6_mmu.sv +${CVA6_REPO_DIR}/core/mmu_unify/cva6_tlb.sv +//${CVA6_REPO_DIR}/core/mmu_unify/cva6_shared_tlb.sv + // end of manifest diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 94e1c4f119..6fc29a3e79 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -170,11 +170,13 @@ module load_store_unit .* ); end else if (MMU_PRESENT && (riscv::XLEN == 32)) begin : gen_mmu_sv32 - cva6_mmu_sv32 #( + cva6_mmu #( .CVA6Cfg (CVA6Cfg), .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH) + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( // misaligned bypass .misaligned_ex_i(misaligned_exception), diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 5c38ad9949..d03dd34d4a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -91,7 +91,7 @@ logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; -tlb_update_sv32_t update_shared_tlb_sv32; +tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; logic itlb_lu_access; riscv::pte_cva6_t itlb_content; @@ -163,7 +163,7 @@ cva6_tlb #( .lu_hit_o (dtlb_lu_hit) ); -cva6_shared_tlb #( +cva6_shared_tlb_sv32 #( .CVA6Cfg (CVA6Cfg), .SHARED_TLB_DEPTH(64), .SHARED_TLB_WAYS (2), @@ -188,8 +188,8 @@ cva6_shared_tlb #( .dtlb_vaddr_i (lsu_vaddr_i), // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), + .itlb_update_o(update_itlb_sv32), + .dtlb_update_o(update_dtlb_sv32), // Performance counters .itlb_miss_o(itlb_miss_o), @@ -201,7 +201,7 @@ cva6_shared_tlb #( .itlb_req_o (itlb_req), // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) + .shared_tlb_update_i(update_shared_tlb_sv32) ); cva6_ptw_sv32 #( @@ -251,11 +251,23 @@ cva6_ptw_sv32 #( ); -assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; -assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; -assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; -assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; -assign update_shared_tlb_sv32.content = update_shared_tlb.content; +// assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; +// assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; +// assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; +// assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; +// assign update_shared_tlb_sv32.content = update_shared_tlb.content; + +assign update_itlb.valid = update_itlb_sv32.valid; +assign update_itlb.is_page = update_itlb_sv32.is_4M; +assign update_itlb.vpn = update_itlb_sv32.vpn; +assign update_itlb.asid = update_itlb_sv32.asid; +assign update_itlb.content = update_itlb_sv32.content; + +assign update_dtlb.valid = update_dtlb_sv32.valid; +assign update_dtlb.is_page = update_dtlb_sv32.is_4M; +assign update_dtlb.vpn = update_dtlb_sv32.vpn; +assign update_dtlb.asid = update_dtlb_sv32.asid; +assign update_dtlb.content = update_dtlb_sv32.content; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -319,7 +331,7 @@ always_comb begin : instr_interface // 4K page icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; // Mega page - if (itlb_is_4M) begin + if (itlb_is_page[0]) begin icache_areq_o.fetch_paddr[21:12] = icache_areq_i.fetch_vaddr[21:12]; end @@ -418,7 +430,7 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_4M_n = dtlb_is_4M; + dtlb_is_4M_n = dtlb_is_page[0]; if (riscv::PLEN > riscv::VLEN) begin lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q}; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 2c0d1c993d..59b56075ae 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -15,7 +15,7 @@ // fully set-associative // This module is an merge of the Sv32 TLB developed by Sebastien // Jacq (Thales Research & Technology) and the Sv39 TLB developed -// by Florian Zaruba and David Schaffenrath to the Sv32 standard. +// by Florian Zaruba and David Schaffenrath. // // =========================================================================== // // Revisions : @@ -50,78 +50,79 @@ import ariane_pkg::*; ); + // Sv32 defines two levels of page tables struct packed { - logic [ASID_LEN-1:0] asid; + logic [ASID_LEN-1:0] asid; logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; logic [PT_LEVELS-2:0] is_page; - logic valid; + logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; riscv::pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; -// logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; -logic [PT_LEVELS-1:0] vpn_match; -logic [PT_LEVELS-1:0] page_match; -logic [PT_LEVELS-1:0] level_match; +logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; +logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; +logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; +logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_vpn_match; +logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_level_match; logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy //------------- // Translation //------------- -always_comb begin : translation +//at level 0 make page match always 1 +//build level match vector according to vpn_match and page_match +//a level has a match if all vpn of higher levels and current have a match, +//AND the page_match is also set +//At level 0 the page match is always set, so this level will have a match +//if all vpn levels match +genvar i,x; + generate + for (i=0; i < TLB_ENTRIES; i++) begin + //identify page_match for all TLB Entries + assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; + for (x=0; x < PT_LEVELS; x++) begin + //identify if vpn matches at all PT levels for all TLB entries + assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //identify if virtual address vpn matches at all PT levels for all TLB entries + assign vaddr_vpn_match[i][x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced + assign tags_n[i].vpn[x] = (update_i.valid & replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_n[i].vpn[x]; + end + end + endgenerate + +always_comb begin : translation // default assignment lu_hit = '{default: 0}; lu_hit_o = 1'b0; lu_content_o = '{default: 0}; - lu_is_page_o = 0; - - + lu_is_page_o = 0; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin - - //at level 0 make page match always 1 - page_match = (tags_q[i].is_page)*2 +1; - - //build level match vector according to vpn_match and page_match - //a level has a match if all vpn of higher levels and current have a match, - //AND the page_match is also set - //At level 0 the page match is also set, so this level will have a match - //if all vpn levels match - for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vpn_match[x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; - level_match[x] = &vpn_match[PT_LEVELS-1:x] & page_match[x]; - end - - - - // first level match, check the ASID flags as well + // first level match, this may be a page, check the ASID flags as well // if the entry is associated to a global address, don't match the ASID (ASID is don't care) - if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g) ) begin - for (int unsigned a = PT_LEVELS-1; a>=0;a--) begin - //find highest level to have a match and break loop - if (level_match[a]) begin - lu_is_page_o[a] = tags_q[i].is_page[a]; - lu_content_o = content_q[i]; - lu_hit_o = 1'b1; - lu_hit[i] = 1'b1; - break; - end - end - + if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g)) begin + // find if there is a match at any level + if (|level_match[i]) begin + lu_is_page_o = tags_q[i].is_page; //the page size is indicated here + lu_content_o = content_q[i]; + lu_hit_o = 1'b1; + lu_hit[i] = 1'b1; + end end end end logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high -logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_vpn_match; -logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_page_match; -logic [PT_LEVELS-1:0][TLB_ENTRIES-1:0] vaddr_level_match; - - assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i); assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i); @@ -135,28 +136,15 @@ always_comb begin : update_flush for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin - //at level 0 make page match always 1 - vaddr_page_match = (tags_q[i].is_page)*2 +1; - - //build vaddr_level_match vector according to vaddr_vpn_match and vaddr_page_match - //a level has a match if all vpn of higher levels and current have a match, - //AND the page_match is also set - //At level 0 the vaddr_page_match is also set, so this level will have a match - //if all vaddr vpn levels match - for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vaddr_vpn_match[x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[x].vpn; - level_match[x] = &vaddr_vpn_match[PT_LEVELS-1:x] & vaddr_page_match[x]; - end - if (flush_i) begin // invalidate logic // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0; // flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages - else if (asid_to_be_flushed_is0 && (level_match!=0) && (~vaddr_to_be_flushed_is0)) + else if (asid_to_be_flushed_is0 && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) - else if ((!content_q[i].g) && (level_match!=0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + else if ((!content_q[i].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) else if ((!content_q[i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!asid_to_be_flushed_is0)) @@ -164,12 +152,10 @@ always_comb begin : update_flush // normal replacement end else if (update_i.valid & replace_en[i]) begin // update tag array - tags_n[i] = '{ - asid: update_i.asid, - vpn : update_i.vpn, - is_page: update_i.is_page, - valid: 1'b1 - }; + tags_n[i].asid = update_i.asid; + tags_n[i].is_page= update_i.is_page; + tags_n[i].valid = update_i.valid; + // and content as well content_n[i] = update_i.content; end diff --git a/src_files.yml b/src_files.yml index 84173c67ca..7485c3f3ad 100644 --- a/src_files.yml +++ b/src_files.yml @@ -36,6 +36,7 @@ ariane: src/miss_handler.sv, src/mmu_sv39/mmu.sv, src/mmu_sv32/cva6_mmu_sv32.sv, + src/mmu_unify/cva6_mmu.sv, src/mult.sv, src/nbdcache.sv, src/vdregs.sv, @@ -51,6 +52,7 @@ ariane: src/store_unit.sv, src/mmu_sv39/tlb.sv, src/mmu_sv32/cva6_tlb_sv32.sv, + src/mmu_unify/cva6_tlb.sv, src/acc_dispatcher.sv, src/debug/dm_csrs.sv, src/debug/dm_mem.sv, From 34a72cc3ec03647c30e85e1d1e26eeda7504c953 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 24 Nov 2023 17:52:53 +0100 Subject: [PATCH 08/64] unify shared TLB - dv-riscv-mmu-sv32-test.sh passes correctly --- core/Flist.cva6 | 2 +- core/mmu_unify/cva6_mmu.sv | 48 +-- core/mmu_unify/cva6_shared_tlb.sv | 585 +++++++++++++++--------------- 3 files changed, 314 insertions(+), 321 deletions(-) diff --git a/core/Flist.cva6 b/core/Flist.cva6 index 6ec104b08c..9120a28a72 100644 --- a/core/Flist.cva6 +++ b/core/Flist.cva6 @@ -190,6 +190,6 @@ ${CVA6_REPO_DIR}/core/mmu_sv32/cva6_shared_tlb_sv32.sv // MMU Unify ${CVA6_REPO_DIR}/core/mmu_unify/cva6_mmu.sv ${CVA6_REPO_DIR}/core/mmu_unify/cva6_tlb.sv -//${CVA6_REPO_DIR}/core/mmu_unify/cva6_shared_tlb.sv +${CVA6_REPO_DIR}/core/mmu_unify/cva6_shared_tlb.sv // end of manifest diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d03dd34d4a..cdd1c732c7 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -91,7 +91,7 @@ logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; -tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; +tlb_update_sv32_t update_shared_tlb_sv32; logic itlb_lu_access; riscv::pte_cva6_t itlb_content; @@ -163,11 +163,13 @@ cva6_tlb #( .lu_hit_o (dtlb_lu_hit) ); -cva6_shared_tlb_sv32 #( +cva6_shared_tlb #( .CVA6Cfg (CVA6Cfg), .SHARED_TLB_DEPTH(64), .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH) + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) ) i_shared_tlb ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -188,8 +190,8 @@ cva6_shared_tlb_sv32 #( .dtlb_vaddr_i (lsu_vaddr_i), // to TLBs, update logic - .itlb_update_o(update_itlb_sv32), - .dtlb_update_o(update_dtlb_sv32), + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), // Performance counters .itlb_miss_o(itlb_miss_o), @@ -201,7 +203,7 @@ cva6_shared_tlb_sv32 #( .itlb_req_o (itlb_req), // to update shared tlb - .shared_tlb_update_i(update_shared_tlb_sv32) + .shared_tlb_update_i(update_shared_tlb) ); cva6_ptw_sv32 #( @@ -251,23 +253,23 @@ cva6_ptw_sv32 #( ); -// assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; -// assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; -// assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; -// assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; -// assign update_shared_tlb_sv32.content = update_shared_tlb.content; - -assign update_itlb.valid = update_itlb_sv32.valid; -assign update_itlb.is_page = update_itlb_sv32.is_4M; -assign update_itlb.vpn = update_itlb_sv32.vpn; -assign update_itlb.asid = update_itlb_sv32.asid; -assign update_itlb.content = update_itlb_sv32.content; - -assign update_dtlb.valid = update_dtlb_sv32.valid; -assign update_dtlb.is_page = update_dtlb_sv32.is_4M; -assign update_dtlb.vpn = update_dtlb_sv32.vpn; -assign update_dtlb.asid = update_dtlb_sv32.asid; -assign update_dtlb.content = update_dtlb_sv32.content; +assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; +assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; +assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; +assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; +assign update_shared_tlb.content = update_shared_tlb_sv32.content; + +// assign update_itlb.valid = update_itlb_sv32.valid; +// assign update_itlb.is_page = update_itlb_sv32.is_4M; +// assign update_itlb.vpn = update_itlb_sv32.vpn; +// assign update_itlb.asid = update_itlb_sv32.asid; +// assign update_itlb.content = update_itlb_sv32.content; + +// assign update_dtlb.valid = update_dtlb_sv32.valid; +// assign update_dtlb.is_page = update_dtlb_sv32.is_4M; +// assign update_dtlb.vpn = update_dtlb_sv32.vpn; +// assign update_dtlb.asid = update_dtlb_sv32.asid; +// assign update_dtlb.content = update_dtlb_sv32.content; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 86673fb5cb..a1d8a1ad95 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -18,218 +18,207 @@ /* verilator lint_off WIDTH */ module cva6_shared_tlb -import ariane_pkg::*; -#( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int SHARED_TLB_DEPTH = 64, - parameter int SHARED_TLB_WAYS = 2, - parameter int ASID_WIDTH = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 -) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic flush_i, - - input logic enable_translation_i, // CSRs indicate to enable SV32 - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - - input logic [ASID_WIDTH-1:0] asid_i, - - // from TLBs - // did we miss? - input logic itlb_access_i, - input logic itlb_hit_i, - input logic [riscv::VLEN-1:0] itlb_vaddr_i, - - input logic dtlb_access_i, - input logic dtlb_hit_i, - input logic [riscv::VLEN-1:0] dtlb_vaddr_i, - - // to TLBs, update logic - output tlb_update_cva6_t itlb_update_o, - output tlb_update_cva6_t dtlb_update_o, - - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - - output logic shared_tlb_access_o, - output logic shared_tlb_hit_o, - output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, - - output logic itlb_req_o, - - // Update shared TLB in case of miss - input tlb_update_cva6_t shared_tlb_update_i + import ariane_pkg::*; + #( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int SHARED_TLB_DEPTH = 64, + parameter int SHARED_TLB_WAYS = 2, + parameter int ASID_WIDTH = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 + ) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, + + input logic enable_translation_i, // CSRs indicate to enable ??? + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + + input logic [ASID_WIDTH-1:0] asid_i, + + // from TLBs + // did we miss? + input logic itlb_access_i, + input logic itlb_hit_i, + input logic [riscv::VLEN-1:0] itlb_vaddr_i, + + input logic dtlb_access_i, + input logic dtlb_hit_i, + input logic [riscv::VLEN-1:0] dtlb_vaddr_i, + + // to TLBs, update logic + output tlb_update_cva6_t itlb_update_o, + output tlb_update_cva6_t dtlb_update_o, + + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + + output logic shared_tlb_access_o, + output logic shared_tlb_hit_o, + output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, + + output logic itlb_req_o, + + // Update shared TLB in case of miss + input tlb_update_cva6_t shared_tlb_update_i ); -function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(SHARED_TLB_WAYS + function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(SHARED_TLB_WAYS )-1:0] in); - logic [SHARED_TLB_WAYS-1:0] out; - out = '0; - out[in] = 1'b1; - return out; -endfunction + logic [SHARED_TLB_WAYS-1:0] out; + out = '0; + out[in] = 1'b1; + return out; + endfunction -typedef struct packed { - logic [ASID_LEN-1:0] asid; // - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [PT_LEVELS-2:0] is_page; -} shared_tag_t; + typedef struct packed { + logic [ASID_LEN-1:0] asid; //9 bits wide + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; + } shared_tag_t; -shared_tag_t shared_tag_wr; -shared_tag_t [SHARED_TLB_WAYS-1:0] shared_tag_rd; + shared_tag_t shared_tag_wr; + shared_tag_t [SHARED_TLB_WAYS-1:0] shared_tag_rd; -logic [SHARED_TLB_DEPTH-1:0][SHARED_TLB_WAYS-1:0] shared_tag_valid_q, shared_tag_valid_d; + logic [SHARED_TLB_DEPTH-1:0][SHARED_TLB_WAYS-1:0] shared_tag_valid_q, shared_tag_valid_d; -logic [ SHARED_TLB_WAYS-1:0] shared_tag_valid; + logic [ SHARED_TLB_WAYS-1:0] shared_tag_valid; -logic [ SHARED_TLB_WAYS-1:0] tag_wr_en; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_wr_addr; -logic [ $bits(shared_tag_t)-1:0] tag_wr_data; + logic [ SHARED_TLB_WAYS-1:0] tag_wr_en; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_wr_addr; + logic [ $bits(shared_tag_t)-1:0] tag_wr_data; -logic [ SHARED_TLB_WAYS-1:0] tag_rd_en; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_rd_addr; -logic [ $bits(shared_tag_t)-1:0] tag_rd_data [SHARED_TLB_WAYS-1:0]; + logic [ SHARED_TLB_WAYS-1:0] tag_rd_en; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_rd_addr; + logic [ $bits(shared_tag_t)-1:0] tag_rd_data [SHARED_TLB_WAYS-1:0]; -logic [ SHARED_TLB_WAYS-1:0] tag_req; -logic [ SHARED_TLB_WAYS-1:0] tag_we; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_addr; + logic [ SHARED_TLB_WAYS-1:0] tag_req; + logic [ SHARED_TLB_WAYS-1:0] tag_we; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_addr; -logic [ SHARED_TLB_WAYS-1:0] pte_wr_en; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr; -logic [$bits(riscv::pte_cva6_t)-1:0] pte_wr_data; + logic [ SHARED_TLB_WAYS-1:0] pte_wr_en; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr; + logic [$bits(riscv::pte_cva6_t)-1:0] pte_wr_data; -logic [ SHARED_TLB_WAYS-1:0] pte_rd_en; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr; -logic [$bits(riscv::pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; + logic [ SHARED_TLB_WAYS-1:0] pte_rd_en; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr; + logic [$bits(riscv::pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; -logic [ SHARED_TLB_WAYS-1:0] pte_req; -logic [ SHARED_TLB_WAYS-1:0] pte_we; -logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_addr; + logic [ SHARED_TLB_WAYS-1:0] pte_req; + logic [ SHARED_TLB_WAYS-1:0] pte_we; + logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_addr; -logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn_d,vpn_q; -logic [PT_LEVELS-1:0] vpn_match; -logic [PT_LEVELS-1:0] page_match; -logic [PT_LEVELS-1:0] level_match; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn_d,vpn_q; + logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] vpn_match; + logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] page_match; + logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] level_match; -riscv::pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; + riscv::pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; -logic [riscv::VLEN-1-12:0] itlb_vpn_q; -logic [riscv::VLEN-1-12:0] dtlb_vpn_q; + logic [riscv::VLEN-1-12:0] itlb_vpn_q; + logic [riscv::VLEN-1-12:0] dtlb_vpn_q; -logic [ASID_WIDTH-1:0] tlb_update_asid_q, tlb_update_asid_d; + logic [ASID_WIDTH-1:0] tlb_update_asid_q, tlb_update_asid_d; -logic shared_tlb_access_q, shared_tlb_access_d; -logic shared_tlb_hit_d; -logic [riscv::VLEN-1:0] shared_tlb_vaddr_q, shared_tlb_vaddr_d; + logic shared_tlb_access_q, shared_tlb_access_d; + logic shared_tlb_hit_d; + logic [riscv::VLEN-1:0] shared_tlb_vaddr_q, shared_tlb_vaddr_d; -logic itlb_req_d, itlb_req_q; -logic dtlb_req_d, dtlb_req_q; + logic itlb_req_d, itlb_req_q; + logic dtlb_req_d, dtlb_req_q; -// replacement strategy -logic [SHARED_TLB_WAYS-1:0] way_valid; -logic update_lfsr; // shift the LFSR -logic [$clog2(SHARED_TLB_WAYS)-1:0] inv_way; // first non-valid encountered -logic [$clog2(SHARED_TLB_WAYS)-1:0] rnd_way; // random index for replacement -logic [$clog2(SHARED_TLB_WAYS)-1:0] repl_way; // way to replace -logic [SHARED_TLB_WAYS-1:0] repl_way_oh_d; // way to replace (onehot) -logic all_ways_valid; // we need to switch repl strategy since all are valid + // replacement strategy + logic [SHARED_TLB_WAYS-1:0] way_valid; + logic update_lfsr; // shift the LFSR + logic [$clog2(SHARED_TLB_WAYS)-1:0] inv_way; // first non-valid encountered + logic [$clog2(SHARED_TLB_WAYS)-1:0] rnd_way; // random index for replacement + logic [$clog2(SHARED_TLB_WAYS)-1:0] repl_way; // way to replace + logic [SHARED_TLB_WAYS-1:0] repl_way_oh_d; // way to replace (onehot) + logic all_ways_valid; // we need to switch repl strategy since all are valid -assign shared_tlb_access_o = shared_tlb_access_q; -assign shared_tlb_hit_o = shared_tlb_hit_d; -assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; + assign shared_tlb_access_o = shared_tlb_access_q; + assign shared_tlb_hit_o = shared_tlb_hit_d; + assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; -assign itlb_req_o = itlb_req_q; + assign itlb_req_o = itlb_req_q; -/////////////////////////////////////////////////////// -// tag comparison, hit generation -/////////////////////////////////////////////////////// -always_comb begin : itlb_dtlb_miss - itlb_miss_o = 1'b0; - dtlb_miss_o = 1'b0; - vpn_d = vpn_q; + genvar i,x; + generate + for (i=0; i < SHARED_TLB_WAYS; i++) begin + //identify page_match for all TLB Entries + assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; + for (x=0; x < PT_LEVELS; x++) begin + assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); + assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + end + end + endgenerate - tag_rd_en = '0; - pte_rd_en = '0; - itlb_req_d = 1'b0; - dtlb_req_d = 1'b0; + /////////////////////////////////////////////////////// + // tag comparison, hit generation + /////////////////////////////////////////////////////// + always_comb begin : itlb_dtlb_miss + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; - tlb_update_asid_d = tlb_update_asid_q; + tag_rd_en = '0; + pte_rd_en = '0; - shared_tlb_access_d = '0; - shared_tlb_vaddr_d = shared_tlb_vaddr_q; + itlb_req_d = 1'b0; + dtlb_req_d = 1'b0; - tag_rd_addr = '0; - pte_rd_addr = '0; + tlb_update_asid_d = tlb_update_asid_q; - // if we got an ITLB miss - if (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin - tag_rd_en = '1; - tag_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; - pte_rd_en = '1; - pte_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + shared_tlb_access_d = '0; + shared_tlb_vaddr_d = shared_tlb_vaddr_q; - for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vpn[x] = itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; - end + tag_rd_addr = '0; + pte_rd_addr = '0; - itlb_miss_o = 1'b1; - itlb_req_d = 1'b1; + // if we got an ITLB miss + if (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin + tag_rd_en = '1; + tag_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + pte_rd_en = '1; + pte_rd_addr = itlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; - tlb_update_asid_d = asid_i; + itlb_miss_o = 1'b1; + itlb_req_d = 1'b1; - shared_tlb_access_d = 1'b1; - shared_tlb_vaddr_d = itlb_vaddr_i; + tlb_update_asid_d = asid_i; - // we got an DTLB miss - end else if (en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) begin - tag_rd_en = '1; - tag_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; - pte_rd_en = '1; - pte_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + shared_tlb_access_d = 1'b1; + shared_tlb_vaddr_d = itlb_vaddr_i; - for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vpn[x] = itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)]; - end + // we got an DTLB miss + end else if (en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) begin + tag_rd_en = '1; + tag_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; + pte_rd_en = '1; + pte_rd_addr = dtlb_vaddr_i[12+:$clog2(SHARED_TLB_DEPTH)]; - dtlb_miss_o = 1'b1; - dtlb_req_d = 1'b1; + dtlb_miss_o = 1'b1; + dtlb_req_d = 1'b1; - tlb_update_asid_d = asid_i; + tlb_update_asid_d = asid_i; - shared_tlb_access_d = 1'b1; - shared_tlb_vaddr_d = dtlb_vaddr_i; - end -end //itlb_dtlb_miss - -always_comb begin : tag_comparison - shared_tlb_hit_d = 1'b0; - dtlb_update_o = '0; - itlb_update_o = '0; - - //at level 0 make page match always 1 - page_match = (shared_tag_rd[i].is_page)*2 +1; - - //build level match vector according to vpn_match and page_match - //a level has a match if all vpn of higher levels and current have a match, - //AND the page_match is also set - //At level 0 the page match is also set, so this level will have a match - //if all vpn levels match - for (int unsigned x = 0; x < PT_LEVELS; x++) begin - vpn_match[x] = vpn[x] == shared_tag_rd[i].vpn[x]; - level_match[x] = &vpn_match[PT_LEVELS-1:x] & page_match[x]; - end - //number of ways - for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin - if (shared_tag_valid[i] && ((tlb_update_asid_q == shared_tag_rd[i].asid) || pte[i].g)) begin - for (int unsigned a = PT_LEVELS-1; a>=0;a--) begin - //find highest level to have a match and break loop - if (level_match[a]) begin + shared_tlb_access_d = 1'b1; + shared_tlb_vaddr_d = dtlb_vaddr_i; + end + end //itlb_dtlb_miss + always_comb begin : tag_comparison + shared_tlb_hit_d = 1'b0; + dtlb_update_o = '0; + itlb_update_o = '0; + //number of ways + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + if (shared_tag_valid[i] && ((tlb_update_asid_q == shared_tag_rd[i].asid) || pte[i].g)) begin + if (|level_match[i]) begin shared_tlb_hit_d = 1'b1; if (itlb_req_q) begin itlb_update_o.valid = 1'b1; @@ -247,147 +236,149 @@ always_comb begin : tag_comparison end end end + end //tag_comparison + + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + itlb_vpn_q <= '0; + dtlb_vpn_q <= '0; + tlb_update_asid_q <= '0; + shared_tlb_access_q <= '0; + shared_tlb_vaddr_q <= '0; + shared_tag_valid_q <= '0; + vpn_q <= 0; + itlb_req_q <= '0; + dtlb_req_q <= '0; + shared_tag_valid <= '0; + end else begin + itlb_vpn_q <= itlb_vaddr_i[riscv::SV-1:12]; + dtlb_vpn_q <= dtlb_vaddr_i[riscv::SV-1:12]; + tlb_update_asid_q <= tlb_update_asid_d; + shared_tlb_access_q <= shared_tlb_access_d; + shared_tlb_vaddr_q <= shared_tlb_vaddr_d; + shared_tag_valid_q <= shared_tag_valid_d; + vpn_q <= vpn_d; + itlb_req_q <= itlb_req_d; + dtlb_req_q <= dtlb_req_d; + shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; + end end -end //tag_comparison - -// sequential process -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - itlb_vpn_q <= '0; - dtlb_vpn_q <= '0; - tlb_update_asid_q <= '0; - shared_tlb_access_q <= '0; - shared_tlb_vaddr_q <= '0; - shared_tag_valid_q <= '0; - vpn_q <= 0; - itlb_req_q <= '0; - dtlb_req_q <= '0; - shared_tag_valid <= '0; - end else begin - itlb_vpn_q <= itlb_vaddr_i[riscv::SV-1:12]; - dtlb_vpn_q <= dtlb_vaddr_i[riscv::SV-1:12]; - tlb_update_asid_q <= tlb_update_asid_d; - shared_tlb_access_q <= shared_tlb_access_d; - shared_tlb_vaddr_q <= shared_tlb_vaddr_d; - shared_tag_valid_q <= shared_tag_valid_d; - vpn_q <= vpn_d; - itlb_req_q <= itlb_req_d; - dtlb_req_q <= dtlb_req_d; - shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; - end -end - -// ------------------ -// Update and Flush -// ------------------ -always_comb begin : update_flush - shared_tag_valid_d = shared_tag_valid_q; - tag_wr_en = '0; - pte_wr_en = '0; - - if (flush_i) begin - shared_tag_valid_d = '0; - end else if (shared_tlb_update_i.valid) begin - for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin - if (repl_way_oh_d[i]) begin - shared_tag_valid_d[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]][i] = 1'b1; - tag_wr_en[i] = 1'b1; - pte_wr_en[i] = 1'b1; + + // ------------------ + // Update and Flush + // ------------------ + always_comb begin : update_flush + shared_tag_valid_d = shared_tag_valid_q; + tag_wr_en = '0; + pte_wr_en = '0; + + if (flush_i) begin + shared_tag_valid_d = '0; + end else if (shared_tlb_update_i.valid) begin + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + if (repl_way_oh_d[i]) begin + shared_tag_valid_d[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]][i] = 1'b1; + tag_wr_en[i] = 1'b1; + pte_wr_en[i] = 1'b1; + end end end - end -end //update_flush + end //update_flush + + assign shared_tag_wr.asid = shared_tlb_update_i.asid; + assign shared_tag_wr.vpn[1] = shared_tlb_update_i.vpn[19:10]; + assign shared_tag_wr.vpn[0] = shared_tlb_update_i.vpn[9:0]; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; -assign shared_tag_wr.asid = shared_tlb_update_i.asid; -genvar i; + genvar z; generate - for (i=0; i < PT_LEVELS; i++) begin - assign shared_tag_wr.vpn = shared_tlb_update_i.vpn[1((VPN_LEN/PT_LEVELS)*(x+1))-1:((VPN_LEN/PT_LEVELS)*x)]; + for (z=0; z < PT_LEVELS; z++) begin + assign shared_tag_wr.vpn[z] = shared_tlb_update_i.vpn[((VPN_LEN/PT_LEVELS)*(z+1))-1:((VPN_LEN/PT_LEVELS)*z)]; end endgenerate -assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; -assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; -assign tag_wr_data = shared_tag_wr; + assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; + assign tag_wr_data = shared_tag_wr; -assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; -assign pte_wr_data = shared_tlb_update_i.content; + assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; + assign pte_wr_data = shared_tlb_update_i.content; -assign way_valid = shared_tag_valid_q[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]]; -assign repl_way = (all_ways_valid) ? rnd_way : inv_way; -assign update_lfsr = shared_tlb_update_i.valid & all_ways_valid; -assign repl_way_oh_d = (shared_tlb_update_i.valid) ? shared_tlb_way_bin2oh(repl_way) : '0; + assign way_valid = shared_tag_valid_q[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]]; + assign repl_way = (all_ways_valid) ? rnd_way : inv_way; + assign update_lfsr = shared_tlb_update_i.valid & all_ways_valid; + assign repl_way_oh_d = (shared_tlb_update_i.valid) ? shared_tlb_way_bin2oh(repl_way) : '0; -lzc #( - .WIDTH(SHARED_TLB_WAYS) -) i_lzc ( - .in_i (~way_valid), - .cnt_o (inv_way), - .empty_o(all_ways_valid) -); - -lfsr #( - .LfsrWidth(8), - .OutWidth ($clog2(SHARED_TLB_WAYS)) -) i_lfsr ( - .clk_i (clk_i), - .rst_ni(rst_ni), - .en_i (update_lfsr), - .out_o (rnd_way) -); - -/////////////////////////////////////////////////////// -// memory arrays and regs -/////////////////////////////////////////////////////// - -assign tag_req = tag_wr_en | tag_rd_en; -assign tag_we = tag_wr_en; -assign tag_addr = tag_wr_en ? tag_wr_addr : tag_rd_addr; - -assign pte_req = pte_wr_en | pte_rd_en; -assign pte_we = pte_wr_en; -assign pte_addr = pte_wr_en ? pte_wr_addr : pte_rd_addr; - -for (genvar i = 0; i < SHARED_TLB_WAYS; i++) begin : gen_sram - // Tag RAM - sram #( - .DATA_WIDTH($bits(shared_tag_t)), - .NUM_WORDS (SHARED_TLB_DEPTH) - ) tag_sram ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .req_i (tag_req[i]), - .we_i (tag_we[i]), - .addr_i (tag_addr), - .wuser_i('0), - .wdata_i(tag_wr_data), - .be_i ('1), - .ruser_o(), - .rdata_o(tag_rd_data[i]) + lzc #( + .WIDTH(SHARED_TLB_WAYS) + ) i_lzc ( + .in_i (~way_valid), + .cnt_o (inv_way), + .empty_o(all_ways_valid) ); - assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]); - - // PTE RAM - sram #( - .DATA_WIDTH($bits(riscv::pte_sv32_t)), - .NUM_WORDS (SHARED_TLB_DEPTH) - ) pte_sram ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .req_i (pte_req[i]), - .we_i (pte_we[i]), - .addr_i (pte_addr), - .wuser_i('0), - .wdata_i(pte_wr_data), - .be_i ('1), - .ruser_o(), - .rdata_o(pte_rd_data[i]) + lfsr #( + .LfsrWidth(8), + .OutWidth ($clog2(SHARED_TLB_WAYS)) + ) i_lfsr ( + .clk_i (clk_i), + .rst_ni(rst_ni), + .en_i (update_lfsr), + .out_o (rnd_way) ); - assign pte[i] = riscv::pte_cva6_t'(pte_rd_data[i]); -end + + /////////////////////////////////////////////////////// + // memory arrays and regs + /////////////////////////////////////////////////////// + + assign tag_req = tag_wr_en | tag_rd_en; + assign tag_we = tag_wr_en; + assign tag_addr = tag_wr_en ? tag_wr_addr : tag_rd_addr; + + assign pte_req = pte_wr_en | pte_rd_en; + assign pte_we = pte_wr_en; + assign pte_addr = pte_wr_en ? pte_wr_addr : pte_rd_addr; + + for (genvar i = 0; i < SHARED_TLB_WAYS; i++) begin : gen_sram + // Tag RAM + sram #( + .DATA_WIDTH($bits(shared_tag_t)), + .NUM_WORDS (SHARED_TLB_DEPTH) + ) tag_sram ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (tag_req[i]), + .we_i (tag_we[i]), + .addr_i (tag_addr), + .wuser_i('0), + .wdata_i(tag_wr_data), + .be_i ('1), + .ruser_o(), + .rdata_o(tag_rd_data[i]) + ); + + assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]); + + // PTE RAM + sram #( + .DATA_WIDTH($bits(riscv::pte_cva6_t)), + .NUM_WORDS (SHARED_TLB_DEPTH) + ) pte_sram ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (pte_req[i]), + .we_i (pte_we[i]), + .addr_i (pte_addr), + .wuser_i('0), + .wdata_i(pte_wr_data), + .be_i ('1), + .ruser_o(), + .rdata_o(pte_rd_data[i]) + ); + assign pte[i] = riscv::pte_cva6_t'(pte_rd_data[i]); + end endmodule /* verilator lint_on WIDTH */ From 65bf15bef80f989ff253a60c4594254c3c08c240 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 28 Nov 2023 09:53:10 +0100 Subject: [PATCH 09/64] unified PTW - dv-riscv-mmu-sv32-test.sh passes correctly --- Bender.yml | 3 + ariane.core | 1 + core/Flist.cva6 | 1 + core/mmu_unify/cva6_mmu.sv | 20 +- core/mmu_unify/cva6_ptw.sv | 416 ++++++++++++++++++++++++++++++ core/mmu_unify/cva6_shared_tlb.sv | 4 +- src_files.yml | 1 + 7 files changed, 435 insertions(+), 11 deletions(-) create mode 100644 core/mmu_unify/cva6_ptw.sv diff --git a/Bender.yml b/Bender.yml index 98e9e7854c..1f978d1b97 100644 --- a/Bender.yml +++ b/Bender.yml @@ -51,6 +51,7 @@ sources: - core/mmu_unify/cva6_tlb.sv - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv + - core/mmu_unify/cva6_ptw.sv - core/cva6_accel_first_pass_decoder_stub.sv - target: cv32a6_imac_sv32 @@ -61,6 +62,7 @@ sources: - core/mmu_unify/cva6_tlb.sv - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv + - core/mmu_unify/cva6_ptw.sv - core/cva6_accel_first_pass_decoder_stub.sv - target: cv32a6_imafc_sv32 @@ -71,6 +73,7 @@ sources: - core/mmu_unify/cva6_tlb.sv - core/mmu_unify/cva6_mmu.sv - core/mmu_sv32/cva6_ptw_sv32.sv + - core/mmu_unify/cva6_ptw.sv - core/cva6_accel_first_pass_decoder_stub.sv # included via target core/include/${TARGET_CFG}_config_pkg.sv diff --git a/ariane.core b/ariane.core index f189cb7ace..577605276a 100644 --- a/ariane.core +++ b/ariane.core @@ -42,6 +42,7 @@ filesets: - src/perf_counters.sv - src/mmu_sv39/ptw.sv - src/mmu_sv32/cva6_ptw_sv32.sv + - src/mmu_unify/cva6_ptw.sv - src/regfile_ff.sv - src/scoreboard.sv - src/store_buffer.sv diff --git a/core/Flist.cva6 b/core/Flist.cva6 index 9120a28a72..aa8f20b281 100644 --- a/core/Flist.cva6 +++ b/core/Flist.cva6 @@ -191,5 +191,6 @@ ${CVA6_REPO_DIR}/core/mmu_sv32/cva6_shared_tlb_sv32.sv ${CVA6_REPO_DIR}/core/mmu_unify/cva6_mmu.sv ${CVA6_REPO_DIR}/core/mmu_unify/cva6_tlb.sv ${CVA6_REPO_DIR}/core/mmu_unify/cva6_shared_tlb.sv +${CVA6_REPO_DIR}/core/mmu_unify/cva6_ptw.sv // end of manifest diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index cdd1c732c7..1722366da6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -91,7 +91,7 @@ logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; -tlb_update_sv32_t update_shared_tlb_sv32; +// tlb_update_sv32_t update_shared_tlb_sv32; logic itlb_lu_access; riscv::pte_cva6_t itlb_content; @@ -206,9 +206,11 @@ cva6_shared_tlb #( .shared_tlb_update_i(update_shared_tlb) ); -cva6_ptw_sv32 #( +cva6_ptw #( .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH) + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) ) i_ptw ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -225,7 +227,7 @@ cva6_ptw_sv32 #( .req_port_o (req_port_o), // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb_sv32), + .shared_tlb_update_o(update_shared_tlb), .update_vaddr_o(update_vaddr), @@ -253,11 +255,11 @@ cva6_ptw_sv32 #( ); -assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; -assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; -assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; -assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; -assign update_shared_tlb.content = update_shared_tlb_sv32.content; +// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; +// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; +// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; +// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; +// assign update_shared_tlb.content = update_shared_tlb_sv32.content; // assign update_itlb.valid = update_itlb_sv32.valid; // assign update_itlb.is_page = update_itlb_sv32.is_4M; diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv new file mode 100644 index 0000000000..3891c7b734 --- /dev/null +++ b/core/mmu_unify/cva6_ptw.sv @@ -0,0 +1,416 @@ +// Copyright (c) 2021 Thales. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Angela Gonzalez PlanV Technology +// Date: 27/11/2023 +// +// +// Description: Hardware-PTW (Page-Table-Walker) for unified MMU. +// This module is an merge of the Sv32 PTW developed by Sebastien +// Jacq (Thales Research & Technology) and the Sv39 PTW developed +// by Florian Zaruba and David Schaffenrath. +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2023-11-27 0.1 A.Gonzalez PTW UNIFIED for CVA6 +// =========================================================================== // + +/* verilator lint_off WIDTH */ + +module cva6_ptw + import ariane_pkg::*; + #( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int ASID_WIDTH = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 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 + output logic ptw_active_o, + output logic walking_instr_o, // set when walking for TLB + output logic ptw_error_o, // set when an error occurred + output logic ptw_access_exception_o, // set when an PMP access exception occured + + input logic lsu_is_store_i, // this translation was triggered by a store + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + + // to Shared TLB, update logic + output tlb_update_cva6_t shared_tlb_update_o, + + output logic [riscv::VLEN-1:0] update_vaddr_o, + + input logic [ASID_WIDTH-1:0] asid_i, + + // from shared TLB + input logic shared_tlb_access_i, + input logic shared_tlb_hit_i, + input logic [riscv::VLEN-1:0] shared_tlb_vaddr_i, + + input logic itlb_req_i, + + // from CSR file + input logic [riscv::PPNW-1:0] satp_ppn_i, // ppn from satp + input logic mxr_i, + + // Performance counters + output logic shared_tlb_miss_o, + + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, + output logic [riscv::PLEN-1:0] bad_paddr_o + +); + + // input registers + logic data_rvalid_q; + riscv::xlen_t data_rdata_q; + + riscv::pte_cva6_t pte; + assign pte = riscv::pte_cva6_t'(data_rdata_q); + + + enum logic [2:0] { + IDLE, + WAIT_GRANT, + PTE_LOOKUP, + WAIT_RVALID, + PROPAGATE_ERROR, + PROPAGATE_ACCESS_ERROR, + LATENCY + } + state_q, state_d; + + // page tables levels + logic [PT_LEVELS-1:0] ptw_lvl_q, ptw_lvl_n,misaligned_page; + + // is this an instruction page table walk? + logic is_instr_ptw_q, is_instr_ptw_n; + logic global_mapping_q, global_mapping_n; + // latched tag signal + logic tag_valid_n, tag_valid_q; + // register the ASID + logic [ASID_WIDTH-1:0] tlb_update_asid_q, tlb_update_asid_n; + // register the VPN we need to walk + logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; + logic [PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; + // 4 byte aligned physical pointer + logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; + + // Assignments + assign update_vaddr_o = vaddr_q; + + assign ptw_active_o = (state_q != IDLE); + //assign walking_instr_o = is_instr_ptw_q; + assign walking_instr_o = is_instr_ptw_q; + // directly output the correct physical address + assign req_port_o.address_index = ptw_pptr_q[DCACHE_INDEX_WIDTH-1:0]; + assign req_port_o.address_tag = ptw_pptr_q[DCACHE_INDEX_WIDTH+DCACHE_TAG_WIDTH-1:DCACHE_INDEX_WIDTH]; + // we are never going to kill this request + assign req_port_o.kill_req = '0; + // we are never going to write with the HPTW + assign req_port_o.data_wdata = '0; + // we only issue one single request at a time + assign req_port_o.data_id = '0; + + // ----------- + // Shared TLB Update + // ----------- + assign shared_tlb_update_o.vpn = vaddr_q[riscv::SV-1:12]; + + +genvar x; + generate + for (x=0; x < PT_LEVELS-1; x++) begin + + // update the correct page table level + assign shared_tlb_update_o.is_page[x] = (ptw_lvl_q == (x+1)); + + // check if the ppn is correctly aligned: + // 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault + // exception. + assign misaligned_page[x] = (ptw_lvl_q == (x+1)) && (pte.ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-x)-1:0] != '0); + + //record the vaddr corresponding to each level + assign vaddr_lvl[x] = vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-x-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-x-2))]; + end + endgenerate + + + + + // output the correct ASID + assign shared_tlb_update_o.asid = tlb_update_asid_q; + // set the global mapping bit + assign shared_tlb_update_o.content = pte | (global_mapping_q << 5); + + + assign req_port_o.tag_valid = tag_valid_q; + + logic allow_access; + + assign bad_paddr_o = ptw_access_exception_o ? ptw_pptr_q : 'b0; + + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_ptw ( + .addr_i (ptw_pptr_q), + // PTW access are always checked as if in S-Mode... + .priv_lvl_i (riscv::PRIV_LVL_S), + // ...and they are always loads + .access_type_i(riscv::ACCESS_READ), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (allow_access) + ); + + +// assign req_port_o.data_be = be_gen_32(req_port_o.address_index[1:0], req_port_o.data_size); + + //------------------- + // Page table walker + //------------------- + // A virtual address va is translated into a physical address pa as follows: + // 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39, + // PAGESIZE=2^12 and LEVELS=3.) + // 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For + // Sv32, PTESIZE=4.) + // 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access + // exception. + // 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5. + // Otherwise, this PTE is a pointer to the next level of the page table. + // Let i=i-1. If i < 0, stop and raise an access exception. Otherwise, let + // a = pte.ppn × PAGESIZE and go to step 2. + // 5. A leaf PTE has been found. Determine if the requested memory access + // is allowed by the pte.r, pte.w, and pte.x bits. If not, stop and + // raise an access exception. Otherwise, the translation is successful. + // Set pte.a to 1, and, if the memory access is a store, set pte.d to 1. + // The translated physical address is given as follows: + // - pa.pgoff = va.pgoff. + // - If i > 0, then this is a superpage translation and + // pa.ppn[i-1:0] = va.vpn[i-1:0]. + // - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i]. + always_comb begin : ptw + // default assignments + // PTW memory interface + tag_valid_n = 1'b0; + req_port_o.data_req = 1'b0; + req_port_o.data_size = 2'b10; + req_port_o.data_we = 1'b0; + ptw_error_o = 1'b0; + ptw_access_exception_o = 1'b0; + shared_tlb_update_o.valid = 1'b0; + is_instr_ptw_n = is_instr_ptw_q; + ptw_lvl_n = ptw_lvl_q; + ptw_pptr_n = ptw_pptr_q; + state_d = state_q; + global_mapping_n = global_mapping_q; + // input registers + tlb_update_asid_n = tlb_update_asid_q; + vaddr_n = vaddr_q; + + shared_tlb_miss_o = 1'b0; + + case (state_q) + + IDLE: begin + // by default we start with the top-most page table + ptw_lvl_n = 1; + global_mapping_n = 1'b0; + is_instr_ptw_n = 1'b0; + // if we got a Shared TLB miss + if (shared_tlb_access_i & ~shared_tlb_hit_i) begin + ptw_pptr_n = { + satp_ppn_i, shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0) + }; // SATP.PPN * PAGESIZE + VPN*PTESIZE = SATP.PPN * 2^(12) + VPN*4 + is_instr_ptw_n = itlb_req_i; + tlb_update_asid_n = asid_i; + vaddr_n = shared_tlb_vaddr_i; + state_d = WAIT_GRANT; + shared_tlb_miss_o = 1'b1; + end + end + WAIT_GRANT: begin + // send a request out + req_port_o.data_req = 1'b1; + // wait for the WAIT_GRANT + if (req_port_i.data_gnt) begin + // send the tag valid signal one cycle later + tag_valid_n = 1'b1; + state_d = PTE_LOOKUP; + end + end + + PTE_LOOKUP: begin + // we wait for the valid signal + if (data_rvalid_q) begin + + // check if the global mapping bit is set + if (pte.g) global_mapping_n = 1'b1; + + // ------------- + // Invalid PTE + // ------------- + // If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise a page-fault exception. + if (!pte.v || (!pte.r && pte.w)) state_d = PROPAGATE_ERROR; + // ----------- + // Valid PTE + // ----------- + else begin + //state_d = IDLE; + state_d = LATENCY; + // it is a valid PTE + // if pte.r = 1 or pte.x = 1 it is a valid PTE + if (pte.r || pte.x) begin + // Valid translation found (either 4M or 4K entry) + if (is_instr_ptw_q) begin + // ------------ + // Update ITLB + // ------------ + // If page is not executable, we can directly raise an error. This + // 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) state_d = PROPAGATE_ERROR; + else shared_tlb_update_o.valid = 1'b1; + + end else begin + // ------------ + // Update DTLB + // ------------ + // 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 + shared_tlb_update_o.valid = 1'b1; + end else begin + state_d = 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 + shared_tlb_update_o.valid = 1'b0; + state_d = PROPAGATE_ERROR; + end + end + //if there is a misaligned page, propagate error + if (|misaligned_page) begin + state_d = PROPAGATE_ERROR; + shared_tlb_update_o.valid = 1'b0; + end + // this is a pointer to the next TLB level + end else begin + // pointer to next level of page table + + if (ptw_lvl_q == PT_LEVELS) begin + // Should already be the last level page table => Error + ptw_lvl_n = PT_LEVELS; + state_d = PROPAGATE_ERROR; + end + else begin + // if (ptw_lvl_q == 1) begin + // we are in the second level now + ptw_lvl_n = ptw_lvl_q+1; + ptw_pptr_n = {pte.ppn, vaddr_lvl[ptw_lvl_q-1], (PT_LEVELS)'(0)}; + state_d = WAIT_GRANT; + // end + end + end + end + + // Check if this access was actually allowed from a PMP perspective + if (!allow_access) begin + shared_tlb_update_o.valid = 1'b0; + // we have to return the failed address in bad_addr + ptw_pptr_n = ptw_pptr_q; + state_d = PROPAGATE_ACCESS_ERROR; + end + end + // we've got a data WAIT_GRANT so tell the cache that the tag is valid + end + // Propagate error to MMU/LSU + PROPAGATE_ERROR: begin + state_d = LATENCY; + ptw_error_o = 1'b1; + end + PROPAGATE_ACCESS_ERROR: begin + state_d = LATENCY; + ptw_access_exception_o = 1'b1; + end + // wait for the rvalid before going back to IDLE + WAIT_RVALID: begin + if (data_rvalid_q) state_d = IDLE; + end + LATENCY: begin + state_d = IDLE; + end + default: begin + state_d = IDLE; + end + endcase + + // ------- + // Flush + // ------- + // should we have flushed before we got an rvalid, wait for it until going back to IDLE + if (flush_i) begin + // on a flush check whether we are + // 1. in the PTE Lookup check whether we still need to wait for an rvalid + // 2. waiting for a grant, if so: wait for it + // if not, go back to idle + if (((state_q inside {PTE_LOOKUP, WAIT_RVALID}) && !data_rvalid_q) || + ((state_q == WAIT_GRANT) && req_port_i.data_gnt)) + state_d = WAIT_RVALID; + else state_d = LATENCY; + end + end + + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + state_q <= IDLE; + is_instr_ptw_q <= 1'b0; + ptw_lvl_q <= 1; + tag_valid_q <= 1'b0; + tlb_update_asid_q <= '0; + vaddr_q <= '0; + ptw_pptr_q <= '0; + global_mapping_q <= 1'b0; + data_rdata_q <= '0; + data_rvalid_q <= 1'b0; + end else begin + state_q <= state_d; + ptw_pptr_q <= ptw_pptr_n; + is_instr_ptw_q <= is_instr_ptw_n; + ptw_lvl_q <= ptw_lvl_n; + tag_valid_q <= tag_valid_n; + tlb_update_asid_q <= tlb_update_asid_n; + vaddr_q <= vaddr_n; + global_mapping_q <= global_mapping_n; + data_rdata_q <= req_port_i.data_rdata; + data_rvalid_q <= req_port_i.data_rvalid; + end + end + +endmodule +/* verilator lint_on WIDTH */ diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index a1d8a1ad95..3ca2ac645f 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -8,8 +8,8 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Sebastien Jacq - Thales Research & Technology -// Date: 08/03/2023 +// Author: Angela Gonzalez PlanV Technology +// Date: 24/11/2023 // // Description: N-way associative shared TLB, it allows to reduce the number // of ITLB and DTLB entries. diff --git a/src_files.yml b/src_files.yml index 7485c3f3ad..b391245227 100644 --- a/src_files.yml +++ b/src_files.yml @@ -46,6 +46,7 @@ ariane: src/perf_counters.sv, src/mmu_sv39/ptw.sv, src/mmu_sv32/cva6_ptw_sv32.sv, + src/mmu_unify/cva6_ptw.sv, src/re_name.sv, src/scoreboard.sv, src/store_buffer.sv, From d8f7705603e8be106fe0f0d51a16c709deada9de Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 29 Nov 2023 12:04:15 +0100 Subject: [PATCH 10/64] unified mmu top level - dv-riscv-mmu-sv32-test.sh passes correctly --- core/mmu_unify/cva6_mmu.sv | 1088 ++++++++++++++++++------------------ core/mmu_unify/cva6_ptw.sv | 2 +- 2 files changed, 545 insertions(+), 545 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1722366da6..d5621087a8 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -8,585 +8,585 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Sebastien Jacq Thales Research & Technology -// Date: 17/07/2021 +// Author: Angela Gonzalez PlanV Technology +// Date: 28/11/2023 // -// Additional contributions by: -// Sebastien Jacq - sjthales on github.com // -// Description: Memory Management Unit for CV32A6, contains TLB and -// address translation unit. Sv32 as defined in RISC-V -// privilege specification 1.11-WIP. -// This module is an adaptation of the MMU Sv39 developed +// Description: Memory Management Unit for CVA6, contains TLB and +// address translation unit. It can be configured for +// Sv32 or Sv39 as defined in RISC-V privilege specification +// 1.11. +// This module is a merge of the MMU Sv32 developed by Sebastien +// Jacq (Thales Research & Technology) and the MMU Sv39 developed // by Florian Zaruba to the Sv32 standard. // // =========================================================================== // // Revisions : // Date Version Author Description -// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 +// 2023-11-28 0.1 A.Gonzalez MMU UNIFIED for CVA6 // =========================================================================== // module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 2, - parameter int unsigned DATA_TLB_ENTRIES = 2, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i -); - -logic iaccess_err; // insufficient privilege to access this instruction page -logic daccess_err; // insufficient privilege to access this data page -logic ptw_active; // PTW is currently walking a page table -logic walking_instr; // PTW is walking because of an ITLB miss -logic ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - -logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; -// tlb_update_sv32_t update_shared_tlb_sv32; - -logic itlb_lu_access; -riscv::pte_cva6_t itlb_content; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; - -logic dtlb_lu_access; -riscv::pte_cva6_t dtlb_content; -logic [PT_LEVELS-2:0] dtlb_is_page; -logic dtlb_lu_hit; - -logic shared_tlb_access; -logic [riscv::VLEN-1:0] shared_tlb_vaddr; -logic shared_tlb_hit; - -logic itlb_req; - - -// Assignments -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); - -cva6_shared_tlb #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), - + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) -); - -cva6_ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), + output logic itlb_miss_o, + output logic dtlb_miss_o, // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now - + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) - + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; -// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; -// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; -// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; -// assign update_shared_tlb.content = update_shared_tlb_sv32.content; - -// assign update_itlb.valid = update_itlb_sv32.valid; -// assign update_itlb.is_page = update_itlb_sv32.is_4M; -// assign update_itlb.vpn = update_itlb_sv32.vpn; -// assign update_itlb.asid = update_itlb_sv32.asid; -// assign update_itlb.content = update_itlb_sv32.content; - -// assign update_dtlb.valid = update_dtlb_sv32.valid; -// assign update_dtlb.is_page = update_dtlb_sv32.is_4M; -// assign update_dtlb.vpn = update_dtlb_sv32.vpn; -// assign update_dtlb.asid = update_dtlb_sv32.asid; -// assign update_dtlb.content = update_dtlb_sv32.content; - -// ila_1 i_ila_1 ( -// .clk(clk_i), // input wire clk -// .probe0({req_port_o.address_tag, req_port_o.address_index}), -// .probe1(req_port_o.data_req), // input wire [63:0] probe1 -// .probe2(req_port_i.data_gnt), // input wire [0:0] probe2 -// .probe3(req_port_i.data_rdata), // input wire [0:0] probe3 -// .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 -// .probe5(ptw_error), // input wire [1:0] probe5 -// .probe6(update_vaddr), // input wire [0:0] probe6 -// .probe7(update_itlb.valid), // input wire [0:0] probe7 -// .probe8(update_dtlb.valid), // input wire [0:0] probe8 -// .probe9(dtlb_lu_access), // input wire [0:0] probe9 -// .probe10(lsu_vaddr_i), // input wire [0:0] probe10 -// .probe11(dtlb_lu_hit), // input wire [0:0] probe11 -// .probe12(itlb_lu_access), // input wire [0:0] probe12 -// .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13 -// .probe14(itlb_lu_hit) // input wire [0:0] probe13 -// ); - -//----------------------- -// Instruction Interface -//----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; - -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - if (riscv::PLEN > riscv::VLEN) - icache_areq_o.fetch_paddr = { - {riscv::PLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr - }; // play through in case we disabled address translation - else - icache_areq_o.fetch_paddr = {2'b00, icache_areq_i.fetch_vaddr[riscv::VLEN-1:0]};// play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - icache_areq_o.fetch_valid = 1'b0; - - // 4K page - icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // Mega page - if (itlb_is_page[0]) begin - icache_areq_o.fetch_paddr[21:12] = icache_areq_i.fetch_vaddr[21:12]; - end - + logic iaccess_err; // insufficient privilege to access this instruction page + logic daccess_err; // insufficient privilege to access this data page + logic ptw_active; // PTW is currently walking a page table + logic walking_instr; // PTW is walking because of an ITLB miss + logic ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + + logic [riscv::VLEN-1:0] update_vaddr; + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + riscv::pte_cva6_t itlb_content; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + + logic dtlb_lu_access; + riscv::pte_cva6_t dtlb_content; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic dtlb_lu_hit; + + logic shared_tlb_access; + logic [riscv::VLEN-1:0] shared_tlb_vaddr; + logic shared_tlb_hit; + + logic itlb_req; + + + // Assignments + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) + ); + + cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + + ); + + // ila_1 i_ila_1 ( + // .clk(clk_i), // input wire clk + // .probe0({req_port_o.address_tag, req_port_o.address_index}), + // .probe1(req_port_o.data_req), // input wire [63:0] probe1 + // .probe2(req_port_i.data_gnt), // input wire [0:0] probe2 + // .probe3(req_port_i.data_rdata), // input wire [0:0] probe3 + // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 + // .probe5(ptw_error), // input wire [1:0] probe5 + // .probe6(update_vaddr), // input wire [0:0] probe6 + // .probe7(update_itlb.valid), // input wire [0:0] probe7 + // .probe8(update_dtlb.valid), // input wire [0:0] probe8 + // .probe9(dtlb_lu_access), // input wire [0:0] probe9 + // .probe10(lsu_vaddr_i), // input wire [0:0] probe10 + // .probe11(dtlb_lu_hit), // input wire [0:0] probe11 + // .probe12(itlb_lu_access), // input wire [0:0] probe12 + // .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13 + // .probe14(itlb_lu_hit) // input wire [0:0] probe13 + // ); + + //----------------------- + // Instruction Interface + //----------------------- + logic match_any_execute_region; + logic pmp_instr_allow; + + assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; + assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + genvar a; + generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // + (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // + itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // + icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; + end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault + endgenerate + + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 - }; //to check on wave --> not connected - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1 - }; //to check on wave --> not connected + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; //to check on wave - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1 - }; //to check on wave --> not connected - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1 - }; //to check on wave --> not connected - end -end - -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); -// Instruction fetch -pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); - -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_sv32_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic dtlb_is_4M_n, dtlb_is_4M_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; -// The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_4M_n = dtlb_is_page[0]; - - if (riscv::PLEN > riscv::VLEN) begin - lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q}; - lsu_dtlb_ppn_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n[riscv::VLEN-1:12]}; - end else begin - lsu_paddr_o = {2'b00, lsu_vaddr_q[riscv::VLEN-1:0]}; - lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PPNW-1:0]; - end - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - // 4K page - lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - lsu_dtlb_ppn_o = dtlb_content.ppn; - // Mega page - if (dtlb_is_4M_q) begin - lsu_paddr_o[21:12] = lsu_vaddr_q[21:12]; - lsu_dtlb_ppn_o[21:12] = lsu_vaddr_n[21:12]; - end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; //to check on wave - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //to check on wave --> not connected + end + end + + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); + + // Instruction fetch + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN ):PPNWMin+1]); + + + + genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + + assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + + endgenerate + + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin lsu_valid_o = 1'b1; - // the page table walker can only throw page faults + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + + // this is a load end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end end - end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end + end + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; end end -end - -// Load/store PMP check -pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_4M_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_4M_q <= dtlb_is_4M_n; + // Load/store PMP check + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); + + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end endmodule diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 3891c7b734..87ab4d35c3 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -13,7 +13,7 @@ // // // Description: Hardware-PTW (Page-Table-Walker) for unified MMU. -// This module is an merge of the Sv32 PTW developed by Sebastien +// This module is a merge of the Sv32 PTW developed by Sebastien // Jacq (Thales Research & Technology) and the Sv39 PTW developed // by Florian Zaruba and David Schaffenrath. // From 76398465f6228ebf3c8190cec2f9a36db9b9b521 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 29 Nov 2023 13:02:21 +0100 Subject: [PATCH 11/64] fix errors in mmu --- core/mmu_unify/cva6_mmu.sv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d5621087a8..fcbf026871 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -278,6 +278,7 @@ module cva6_mmu //----------------------- logic match_any_execute_region; logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -408,13 +409,13 @@ module cva6_mmu // Wires to PMP checks riscv::pmp_access_t pmp_access_type; logic pmp_data_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN ):PPNWMin+1]); + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); From 71a73c76b41aec7951d0b5ac104f6f19cc42ed53 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 29 Nov 2023 14:41:24 +0100 Subject: [PATCH 12/64] modify load_store_unit to instantiate unified mmu also for sv39 --- core/load_store_unit.sv | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 6fc29a3e79..3008a40ebe 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -140,36 +140,36 @@ module load_store_unit // ------------------- // MMU e.g.: TLBs/PTW // ------------------- - if (MMU_PRESENT && (riscv::XLEN == 64)) begin : gen_mmu_sv39 - mmu #( - .CVA6Cfg (CVA6Cfg), - .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), - .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH) - ) i_cva6_mmu ( - // 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), - .lsu_paddr_o (mmu_paddr), - .lsu_exception_o(mmu_exception), - .lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request - .lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request - // connecting PTW to D$ IF - .req_port_i (dcache_req_ports_i[0]), - .req_port_o (dcache_req_ports_o[0]), - // icache address translation requests - .icache_areq_i (icache_areq_i), - .asid_to_be_flushed_i, - .vaddr_to_be_flushed_i, - .icache_areq_o (icache_areq_o), - .pmpcfg_i, - .pmpaddr_i, - .* - ); - end else if (MMU_PRESENT && (riscv::XLEN == 32)) begin : gen_mmu_sv32 + // if (MMU_PRESENT && (riscv::XLEN == 64)) begin : gen_mmu_sv39 + // mmu #( + // .CVA6Cfg (CVA6Cfg), + // .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), + // .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), + // .ASID_WIDTH (ASID_WIDTH) + // ) i_cva6_mmu ( + // // 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), + // .lsu_paddr_o (mmu_paddr), + // .lsu_exception_o(mmu_exception), + // .lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request + // .lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request + // // connecting PTW to D$ IF + // .req_port_i (dcache_req_ports_i[0]), + // .req_port_o (dcache_req_ports_o[0]), + // // icache address translation requests + // .icache_areq_i (icache_areq_i), + // .asid_to_be_flushed_i, + // .vaddr_to_be_flushed_i, + // .icache_areq_o (icache_areq_o), + // .pmpcfg_i, + // .pmpaddr_i, + // .* + // ); + if (MMU_PRESENT) begin : gen_mmu cva6_mmu #( .CVA6Cfg (CVA6Cfg), .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), From 42a7362f244fba437ddc319efc9979de9adea9e4 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 29 Nov 2023 17:34:35 +0100 Subject: [PATCH 13/64] add missing ASID_LEN Parameter at mmu and shared_tlb --- core/load_store_unit.sv | 5 +++-- core/mmu_unify/cva6_mmu.sv | 4 ++++ core/mmu_unify/cva6_shared_tlb.sv | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 3008a40ebe..76ad4fed03 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -175,8 +175,9 @@ module load_store_unit .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .PT_LEVELS (PT_LEVELS) + .ASID_LEN (ariane_pkg::ASID_LEN), + .VPN_LEN (ariane_pkg::VPN_LEN), + .PT_LEVELS (ariane_pkg::PT_LEVELS) ) i_cva6_mmu ( // misaligned bypass .misaligned_ex_i(misaligned_exception), diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index fcbf026871..54f2ca473b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -33,6 +33,7 @@ module cva6_mmu parameter int unsigned INSTR_TLB_ENTRIES = 2, parameter int unsigned DATA_TLB_ENTRIES = 2, parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 ) ( @@ -118,6 +119,7 @@ module cva6_mmu .CVA6Cfg (CVA6Cfg), .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_itlb ( @@ -142,6 +144,7 @@ module cva6_mmu .CVA6Cfg (CVA6Cfg), .TLB_ENTRIES(DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_dtlb ( @@ -167,6 +170,7 @@ module cva6_mmu .SHARED_TLB_DEPTH(64), .SHARED_TLB_WAYS (2), .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_shared_tlb ( diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 3ca2ac645f..d068c8a786 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -24,6 +24,7 @@ module cva6_shared_tlb parameter int SHARED_TLB_DEPTH = 64, parameter int SHARED_TLB_WAYS = 2, parameter int ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 ) ( @@ -74,7 +75,7 @@ module cva6_shared_tlb endfunction typedef struct packed { - logic [ASID_LEN-1:0] asid; //9 bits wide + logic [ASID_LEN-1:0] asid; logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; logic [PT_LEVELS-2:0] is_page; } shared_tag_t; From 835353c606b240f6feb1d7e741752423946925bd Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 29 Nov 2023 18:11:20 +0100 Subject: [PATCH 14/64] correct multiple driver warnings --- core/mmu_unify/cva6_shared_tlb.sv | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index d068c8a786..6864dc66a3 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -151,13 +151,20 @@ module cva6_shared_tlb //identify page_match for all TLB Entries assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; for (x=0; x < PT_LEVELS; x++) begin - assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); + // assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; end end endgenerate + genvar w; + generate + for (w=0; w < PT_LEVELS; w++) begin + assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); + end + endgenerate + /////////////////////////////////////////////////////// // tag comparison, hit generation @@ -288,8 +295,8 @@ module cva6_shared_tlb end //update_flush assign shared_tag_wr.asid = shared_tlb_update_i.asid; - assign shared_tag_wr.vpn[1] = shared_tlb_update_i.vpn[19:10]; - assign shared_tag_wr.vpn[0] = shared_tlb_update_i.vpn[9:0]; + // assign shared_tag_wr.vpn[1] = shared_tlb_update_i.vpn[19:10]; + // assign shared_tag_wr.vpn[0] = shared_tlb_update_i.vpn[9:0]; assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; From 53705c4a6657cb8016315f6278da1e1e067a2190 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 1 Dec 2023 11:37:49 +0100 Subject: [PATCH 15/64] test only tlb unified --- core/mmu_unify/cva6_mmu.sv | 1092 ++++++++++++++++++------------------ 1 file changed, 542 insertions(+), 550 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 54f2ca473b..dc9804144c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -8,590 +8,582 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Angela Gonzalez PlanV Technology -// Date: 28/11/2023 +// Author: Sebastien Jacq Thales Research & Technology +// Date: 17/07/2021 // +// Additional contributions by: +// Sebastien Jacq - sjthales on github.com // -// Description: Memory Management Unit for CVA6, contains TLB and -// address translation unit. It can be configured for -// Sv32 or Sv39 as defined in RISC-V privilege specification -// 1.11. -// This module is a merge of the MMU Sv32 developed by Sebastien -// Jacq (Thales Research & Technology) and the MMU Sv39 developed +// Description: Memory Management Unit for CV32A6, contains TLB and +// address translation unit. Sv32 as defined in RISC-V +// privilege specification 1.11-WIP. +// This module is an adaptation of the MMU Sv39 developed // by Florian Zaruba to the Sv32 standard. // // =========================================================================== // // Revisions : // Date Version Author Description -// 2023-11-28 0.1 A.Gonzalez MMU UNIFIED for CVA6 +// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 // =========================================================================== // module cva6_mmu - import ariane_pkg::*; +import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 2, - parameter int unsigned DATA_TLB_ENTRIES = 2, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i +); + +logic iaccess_err; // insufficient privilege to access this instruction page +logic daccess_err; // insufficient privilege to access this data page +logic ptw_active; // PTW is currently walking a page table +logic walking_instr; // PTW is walking because of an ITLB miss +logic ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + +logic [riscv::VLEN-1:0] update_vaddr; +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; +tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; + +logic itlb_lu_access; +riscv::pte_cva6_t itlb_content; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; + +logic dtlb_lu_access; +riscv::pte_cva6_t dtlb_content; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic dtlb_lu_hit; + +logic shared_tlb_access; +logic [riscv::VLEN-1:0] shared_tlb_vaddr; +logic shared_tlb_hit; + +logic itlb_req; + + +// Assignments +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + +cva6_shared_tlb_sv32 #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH) +) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb_sv32), + .dtlb_update_o(update_dtlb_sv32), + // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb_sv32) +); + +cva6_ptw_sv32 #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH) +) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb_sv32), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + ); - logic iaccess_err; // insufficient privilege to access this instruction page - logic daccess_err; // insufficient privilege to access this data page - logic ptw_active; // PTW is currently walking a page table - logic walking_instr; // PTW is walking because of an ITLB miss - logic ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - - logic [riscv::VLEN-1:0] update_vaddr; - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - riscv::pte_cva6_t itlb_content; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - - logic dtlb_lu_access; - riscv::pte_cva6_t dtlb_content; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic dtlb_lu_hit; - - logic shared_tlb_access; - logic [riscv::VLEN-1:0] shared_tlb_vaddr; - logic shared_tlb_hit; - - logic itlb_req; - - - // Assignments - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - cva6_shared_tlb #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), - - // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) - ); - - cva6_ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), - // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now - - // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) - - ); - - // ila_1 i_ila_1 ( - // .clk(clk_i), // input wire clk - // .probe0({req_port_o.address_tag, req_port_o.address_index}), - // .probe1(req_port_o.data_req), // input wire [63:0] probe1 - // .probe2(req_port_i.data_gnt), // input wire [0:0] probe2 - // .probe3(req_port_i.data_rdata), // input wire [0:0] probe3 - // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 - // .probe5(ptw_error), // input wire [1:0] probe5 - // .probe6(update_vaddr), // input wire [0:0] probe6 - // .probe7(update_itlb.valid), // input wire [0:0] probe7 - // .probe8(update_dtlb.valid), // input wire [0:0] probe8 - // .probe9(dtlb_lu_access), // input wire [0:0] probe9 - // .probe10(lsu_vaddr_i), // input wire [0:0] probe10 - // .probe11(dtlb_lu_hit), // input wire [0:0] probe11 - // .probe12(itlb_lu_access), // input wire [0:0] probe12 - // .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13 - // .probe14(itlb_lu_hit) // input wire [0:0] probe13 - // ); - - //----------------------- - // Instruction Interface - //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // - (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // - itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // - icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; - end +// assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; +// assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; +// assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; +// assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; +// assign update_shared_tlb_sv32.content = update_shared_tlb.content; + +assign update_itlb.valid = update_itlb_sv32.valid; +assign update_itlb.is_page = update_itlb_sv32.is_4M; +assign update_itlb.vpn = update_itlb_sv32.vpn; +assign update_itlb.asid = update_itlb_sv32.asid; +assign update_itlb.content = update_itlb_sv32.content; + +assign update_dtlb.valid = update_dtlb_sv32.valid; +assign update_dtlb.is_page = update_dtlb_sv32.is_4M; +assign update_dtlb.vpn = update_dtlb_sv32.vpn; +assign update_dtlb.asid = update_dtlb_sv32.asid; +assign update_dtlb.content = update_dtlb_sv32.content; + +// ila_1 i_ila_1 ( +// .clk(clk_i), // input wire clk +// .probe0({req_port_o.address_tag, req_port_o.address_index}), +// .probe1(req_port_o.data_req), // input wire [63:0] probe1 +// .probe2(req_port_i.data_gnt), // input wire [0:0] probe2 +// .probe3(req_port_i.data_rdata), // input wire [0:0] probe3 +// .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 +// .probe5(ptw_error), // input wire [1:0] probe5 +// .probe6(update_vaddr), // input wire [0:0] probe6 +// .probe7(update_itlb.valid), // input wire [0:0] probe7 +// .probe8(update_dtlb.valid), // input wire [0:0] probe8 +// .probe9(dtlb_lu_access), // input wire [0:0] probe9 +// .probe10(lsu_vaddr_i), // input wire [0:0] probe10 +// .probe11(dtlb_lu_hit), // input wire [0:0] probe11 +// .probe12(itlb_lu_access), // input wire [0:0] probe12 +// .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13 +// .probe14(itlb_lu_hit) // input wire [0:0] probe13 +// ); + +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + if (riscv::PLEN > riscv::VLEN) + icache_areq_o.fetch_paddr = { + {riscv::PLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr + }; // play through in case we disabled address translation + else + icache_areq_o.fetch_paddr = {2'b00, icache_areq_i.fetch_vaddr[riscv::VLEN-1:0]};// play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end + + icache_areq_o.fetch_valid = 1'b0; + + // 4K page + icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // Mega page + if (itlb_is_page[0]) begin + icache_areq_o.fetch_paddr[21:12] = icache_areq_i.fetch_vaddr[21:12]; + end + - endgenerate - - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 - }; - end - - icache_areq_o.fetch_valid = 1'b0; - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + }; //to check on wave --> not connected + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1 + }; //to check on wave --> not connected end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //to check on wave --> not connected + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; //to check on wave + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1 + }; //to check on wave --> not connected end end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1 + }; //to check on wave --> not connected + end +end - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; - - - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; - - assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); + +// Instruction fetch +pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_sv32_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic dtlb_is_4M_n, dtlb_is_4M_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_4M_n = dtlb_is_page[0]; + + if (riscv::PLEN > riscv::VLEN) begin + lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q}; + lsu_dtlb_ppn_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n[riscv::VLEN-1:12]}; + end else begin + lsu_paddr_o = {2'b00, lsu_vaddr_q[riscv::VLEN-1:0]}; + lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PPNW-1:0]; + end + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + // 4K page + lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; + lsu_dtlb_ppn_o = dtlb_content.ppn; + // Mega page + if (dtlb_is_4M_q) begin + lsu_paddr_o[21:12] = lsu_vaddr_q[21:12]; + lsu_dtlb_ppn_o[21:12] = lsu_vaddr_n[21:12]; + end + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; //to check on wave + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. end - endgenerate - - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1}; end end - end - - // Load/store PMP check - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; end end -endmodule +end + +// Load/store PMP check +pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_4M_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_4M_q <= dtlb_is_4M_n; + end +end +endmodule \ No newline at end of file From bca87bc08e57b0e1eadcc87984f63db961adeff5 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 1 Dec 2023 16:13:48 +0100 Subject: [PATCH 16/64] correct double assignment to tags_n in TLB. correct also ASID_LEN parameter propagation --- core/mmu_unify/cva6_mmu.sv | 2 + core/mmu_unify/cva6_tlb.sv | 231 +++++++++++++++++++------------------ 2 files changed, 118 insertions(+), 115 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index dc9804144c..4bb4fb9351 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -120,6 +120,7 @@ cva6_tlb #( .CVA6Cfg (CVA6Cfg), .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_itlb ( @@ -144,6 +145,7 @@ cva6_tlb #( .CVA6Cfg (CVA6Cfg), .TLB_ENTRIES(DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_dtlb ( diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 59b56075ae..fa8f38c8a4 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -23,138 +23,139 @@ // 2023-11-20 0.1 A.Gonzalez Generic TLB for CVA6 // =========================================================================== // -module cva6_tlb -import ariane_pkg::*; + module cva6_tlb + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 -) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic flush_i, // Flush signal - // Update TLB - input tlb_update_cva6_t update_i, - // Lookup signals - input logic lu_access_i, - input logic [ASID_WIDTH-1:0] lu_asid_i, - input logic [riscv::VLEN-1:0] lu_vaddr_i, - output riscv::pte_cva6_t lu_content_o, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - output logic [PT_LEVELS-2:0] lu_is_page_o, - output logic lu_hit_o + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 + ) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // Flush signal + // Update TLB + input tlb_update_cva6_t update_i, + // Lookup signals + input logic lu_access_i, + input logic [ASID_WIDTH-1:0] lu_asid_i, + input logic [riscv::VLEN-1:0] lu_vaddr_i, + output riscv::pte_cva6_t lu_content_o, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + output logic [PT_LEVELS-2:0] lu_is_page_o, + output logic lu_hit_o ); + // Sv32 defines two levels of page tables, Sv39 defines 3 + struct packed { + logic [ASID_LEN-1:0] asid; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; + logic valid; + } [TLB_ENTRIES-1:0] + tags_q, tags_n; - // Sv32 defines two levels of page tables -struct packed { - logic [ASID_LEN-1:0] asid; - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [PT_LEVELS-2:0] is_page; - logic valid; -} [TLB_ENTRIES-1:0] - tags_q, tags_n; + riscv::pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_vpn_match; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_level_match; + logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic + logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy + //------------- + // Translation + //------------- -riscv::pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_vpn_match; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_level_match; -logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic -logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy -//------------- -// Translation -//------------- + //at level 0 make page match always 1 + //build level match vector according to vpn_match and page_match + //a level has a match if all vpn of higher levels and current have a match, + //AND the page_match is also set + //At level 0 the page match is always set, so this level will have a match + //if all vpn levels match + genvar i,x; + generate + for (i=0; i < TLB_ENTRIES; i++) begin + //identify page_match for all TLB Entries + assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; -//at level 0 make page match always 1 -//build level match vector according to vpn_match and page_match -//a level has a match if all vpn of higher levels and current have a match, -//AND the page_match is also set -//At level 0 the page match is always set, so this level will have a match -//if all vpn levels match -genvar i,x; - generate - for (i=0; i < TLB_ENTRIES; i++) begin - //identify page_match for all TLB Entries - assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; - - for (x=0; x < PT_LEVELS; x++) begin - //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; - //identify if there is a hit at each PT level for all TLB entries - assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; - //identify if virtual address vpn matches at all PT levels for all TLB entries - assign vaddr_vpn_match[i][x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; - //identify if there is a hit at each PT level for all TLB entries - assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; - //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced - assign tags_n[i].vpn[x] = (update_i.valid & replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_n[i].vpn[x]; + for (x=0; x < PT_LEVELS; x++) begin + //identify if vpn matches at all PT levels for all TLB entries + assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //identify if virtual address vpn matches at all PT levels for all TLB entries + assign vaddr_vpn_match[i][x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced + assign tags_n[i].vpn[x] = (!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; + end end - end - endgenerate + endgenerate -always_comb begin : translation + always_comb begin : translation - // default assignment - lu_hit = '{default: 0}; - lu_hit_o = 1'b0; - lu_content_o = '{default: 0}; - lu_is_page_o = 0; + // default assignment + lu_hit = '{default: 0}; + lu_hit_o = 1'b0; + lu_content_o = '{default: 0}; + lu_is_page_o = 0; - for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin - // first level match, this may be a page, check the ASID flags as well - // if the entry is associated to a global address, don't match the ASID (ASID is don't care) - if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g)) begin - // find if there is a match at any level - if (|level_match[i]) begin - lu_is_page_o = tags_q[i].is_page; //the page size is indicated here - lu_content_o = content_q[i]; - lu_hit_o = 1'b1; - lu_hit[i] = 1'b1; + for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin + // first level match, this may be a page, check the ASID flags as well + // if the entry is associated to a global address, don't match the ASID (ASID is don't care) + if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g)) begin + // find if there is a match at any level + if (|level_match[i]) begin + lu_is_page_o = tags_q[i].is_page; //the page size is indicated here + lu_content_o = content_q[i]; + lu_hit_o = 1'b1; + lu_hit[i] = 1'b1; + end end end end -end - -logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high -logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high -assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i); -assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i); + logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high + logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high -// ------------------ -// Update and Flush -// ------------------ -always_comb begin : update_flush - tags_n = tags_q; - content_n = content_q; + assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i); + assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i); - for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin + // ------------------ + // Update and Flush + // ------------------ + always_comb begin : update_flush + + content_n = content_q; - if (flush_i) begin - // invalidate logic - // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) - if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0; - // flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages - else if (asid_to_be_flushed_is0 && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; - // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) - else if ((!content_q[i].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; - // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) - else if ((!content_q[i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; - // normal replacement - end else if (update_i.valid & replace_en[i]) begin - // update tag array - tags_n[i].asid = update_i.asid; - tags_n[i].is_page= update_i.is_page; - tags_n[i].valid = update_i.valid; + for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin + tags_n[i].asid = tags_q[i].asid; + tags_n[i].is_page = tags_q[i].is_page; + tags_n[i].valid = tags_q[i].valid; + if (flush_i) begin + // invalidate logic + // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) + if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0; + // flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages + else if (asid_to_be_flushed_is0 && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) + else if ((!content_q[i].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) + else if ((!content_q[i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid[ASID_WIDTH-1:0]) && (!asid_to_be_flushed_is0)) + tags_n[i].valid = 1'b0; + // normal replacement + end else if (update_i.valid & replace_en[i]) begin + // update tag array + tags_n[i].asid = update_i.asid; + tags_n[i].is_page= update_i.is_page; + tags_n[i].valid = 1'b1; // and content as well content_n[i] = update_i.content; From 7f3e7dfba85d7797d0030fd5fb20d2153f55510a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 4 Dec 2023 11:48:38 +0100 Subject: [PATCH 17/64] complete MMU unified - sv32 boots linux, sv39 does not --- core/mmu_unify/cva6_mmu.sv | 1041 +++++++++++++++-------------- core/mmu_unify/cva6_ptw.sv | 8 +- core/mmu_unify/cva6_shared_tlb.sv | 4 +- 3 files changed, 533 insertions(+), 520 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 4bb4fb9351..59658a6a40 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -27,250 +27,243 @@ // =========================================================================== // module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 2, - parameter int unsigned DATA_TLB_ENTRIES = 2, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i -); - -logic iaccess_err; // insufficient privilege to access this instruction page -logic daccess_err; // insufficient privilege to access this data page -logic ptw_active; // PTW is currently walking a page table -logic walking_instr; // PTW is walking because of an ITLB miss -logic ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - -logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; -tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; - -logic itlb_lu_access; -riscv::pte_cva6_t itlb_content; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; - -logic dtlb_lu_access; -riscv::pte_cva6_t dtlb_content; -logic [PT_LEVELS-2:0] dtlb_is_page; -logic dtlb_lu_hit; - -logic shared_tlb_access; -logic [riscv::VLEN-1:0] shared_tlb_vaddr; -logic shared_tlb_hit; - -logic itlb_req; - - -// Assignments -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); - -cva6_shared_tlb_sv32 #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH) -) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb_sv32), - .dtlb_update_o(update_dtlb_sv32), - + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb_sv32) -); - -cva6_ptw_sv32 #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH) -) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), + output logic itlb_miss_o, + output logic dtlb_miss_o, // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb_sv32), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now - + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) - + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -// assign update_shared_tlb_sv32.valid = update_shared_tlb.valid; -// assign update_shared_tlb_sv32.is_4M = update_shared_tlb.is_page; -// assign update_shared_tlb_sv32.vpn = update_shared_tlb.vpn; -// assign update_shared_tlb_sv32.asid = update_shared_tlb.asid; -// assign update_shared_tlb_sv32.content = update_shared_tlb.content; - -assign update_itlb.valid = update_itlb_sv32.valid; -assign update_itlb.is_page = update_itlb_sv32.is_4M; -assign update_itlb.vpn = update_itlb_sv32.vpn; -assign update_itlb.asid = update_itlb_sv32.asid; -assign update_itlb.content = update_itlb_sv32.content; - -assign update_dtlb.valid = update_dtlb_sv32.valid; -assign update_dtlb.is_page = update_dtlb_sv32.is_4M; -assign update_dtlb.vpn = update_dtlb_sv32.vpn; -assign update_dtlb.asid = update_dtlb_sv32.asid; -assign update_dtlb.content = update_dtlb_sv32.content; + logic iaccess_err; // insufficient privilege to access this instruction page + logic daccess_err; // insufficient privilege to access this data page + logic ptw_active; // PTW is currently walking a page table + logic walking_instr; // PTW is walking because of an ITLB miss + logic ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + + logic [riscv::VLEN-1:0] update_vaddr; + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; + + logic itlb_lu_access; + riscv::pte_cva6_t itlb_content; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + + logic dtlb_lu_access; + riscv::pte_cva6_t dtlb_content; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic dtlb_lu_hit; + + logic shared_tlb_access; + logic [riscv::VLEN-1:0] shared_tlb_vaddr; + logic shared_tlb_hit; + + logic itlb_req; + + + // Assignments + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) + ); + + cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + + ); + +// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; +// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; +// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; +// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; +// assign update_shared_tlb.content = update_shared_tlb_sv32.content; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -291,301 +284,321 @@ assign update_dtlb.content = update_dtlb_sv32.content; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); -//----------------------- -// Instruction Interface -//----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; - -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - if (riscv::PLEN > riscv::VLEN) - icache_areq_o.fetch_paddr = { - {riscv::PLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr - }; // play through in case we disabled address translation - else - icache_areq_o.fetch_paddr = {2'b00, icache_areq_i.fetch_vaddr[riscv::VLEN-1:0]};// play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - icache_areq_o.fetch_valid = 1'b0; - - // 4K page - icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // Mega page - if (itlb_is_page[0]) begin - icache_areq_o.fetch_paddr[21:12] = icache_areq_i.fetch_vaddr[21:12]; - end - + //----------------------- + // Instruction Interface + //----------------------- + logic match_any_execute_region; + logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + + assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; + assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + genvar a; + generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // + (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // + itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // + icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; + end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault + endgenerate + + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 - }; //to check on wave --> not connected - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1 - }; //to check on wave --> not connected + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; //to check on wave - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1 - }; //to check on wave --> not connected - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1 - }; //to check on wave --> not connected - end -end -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); - -// Instruction fetch -pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); - -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_sv32_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic dtlb_is_4M_n, dtlb_is_4M_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; -// The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_4M_n = dtlb_is_page[0]; - - if (riscv::PLEN > riscv::VLEN) begin - lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q}; - lsu_dtlb_ppn_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n[riscv::VLEN-1:12]}; - end else begin - lsu_paddr_o = {2'b00, lsu_vaddr_q[riscv::VLEN-1:0]}; - lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PPNW-1:0]; - end - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - // 4K page - lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - lsu_dtlb_ppn_o = dtlb_content.ppn; - // Mega page - if (dtlb_is_4M_q) begin - lsu_paddr_o[21:12] = lsu_vaddr_q[21:12]; - lsu_dtlb_ppn_o[21:12] = lsu_vaddr_n[21:12]; - end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; //to check on wave - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //to check on wave --> not connected + end + end + + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); + + // Instruction fetch + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; + + + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + + + genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + + assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + + endgenerate + + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin lsu_valid_o = 1'b1; - // the page table walker can only throw page faults + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + + // this is a load end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end end - end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end + end + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; end end -end - -// Load/store PMP check -pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_4M_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_4M_q <= dtlb_is_4M_n; + // Load/store PMP check + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); + + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end endmodule \ No newline at end of file diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 87ab4d35c3..6f252cd143 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -26,8 +26,8 @@ /* verilator lint_off WIDTH */ module cva6_ptw - import ariane_pkg::*; - #( + import ariane_pkg::*; +#( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int ASID_WIDTH = 1, parameter int unsigned VPN_LEN = 1, @@ -36,8 +36,8 @@ module cva6_ptw 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 + // 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 occurred diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 6864dc66a3..ba40875997 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -19,7 +19,7 @@ module cva6_shared_tlb import ariane_pkg::*; - #( +#( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int SHARED_TLB_DEPTH = 64, parameter int SHARED_TLB_WAYS = 2, @@ -27,7 +27,7 @@ module cva6_shared_tlb parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 - ) ( +) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic flush_i, From bf3dd82110b97aa0ff778e642042bde1e89ec537 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 4 Dec 2023 17:43:59 +0100 Subject: [PATCH 18/64] Correct size assignment for PTE Correct value assignment req_port_o.data_be and req_port_o.data_be for the sv39 case. --- core/mmu_unify/cva6_ptw.sv | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 6f252cd143..15c28a6f23 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -81,7 +81,7 @@ module cva6_ptw riscv::xlen_t data_rdata_q; riscv::pte_cva6_t pte; - assign pte = riscv::pte_cva6_t'(data_rdata_q); + assign pte = riscv::pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); enum logic [2:0] { @@ -183,7 +183,9 @@ genvar x; ); -// assign req_port_o.data_be = be_gen_32(req_port_o.address_index[1:0], req_port_o.data_size); + assign req_port_o.data_be = riscv::XLEN ==32? + be_gen_32(req_port_o.address_index[1:0], req_port_o.data_size): + be_gen(req_port_o.address_index[2:0], req_port_o.data_size); //------------------- // Page table walker @@ -213,7 +215,7 @@ genvar x; // PTW memory interface tag_valid_n = 1'b0; req_port_o.data_req = 1'b0; - req_port_o.data_size = 2'b10; + req_port_o.data_size = 2'b{PT_LEVELS}; req_port_o.data_we = 1'b0; ptw_error_o = 1'b0; ptw_access_exception_o = 1'b0; From 2e2ccd788d551dcd1091e524645e7ccc9792d438 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 15:55:55 +0100 Subject: [PATCH 19/64] Correct tlb page_match order --- core/mmu_unify/cva6_tlb.sv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index fa8f38c8a4..cdab5f6275 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -80,9 +80,11 @@ generate for (i=0; i < TLB_ENTRIES; i++) begin //identify page_match for all TLB Entries - assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; + // assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; for (x=0; x < PT_LEVELS; x++) begin + //identify page_match for all TLB Entries + assign page_match[i][x] = x==0 ? 1 :tags_q[i].is_page[PT_LEVELS-1-x]; //identify if vpn matches at all PT levels for all TLB entries assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; //identify if there is a hit at each PT level for all TLB entries From ace52fec7366b3f4447cd45958f8a31c002bfa30 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 16:05:26 +0100 Subject: [PATCH 20/64] correct req_port_o.data_size in ptw --- core/mmu_unify/cva6_ptw.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 15c28a6f23..fb24401f42 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -215,7 +215,7 @@ genvar x; // PTW memory interface tag_valid_n = 1'b0; req_port_o.data_req = 1'b0; - req_port_o.data_size = 2'b{PT_LEVELS}; + req_port_o.data_size = 2'(PT_LEVELS); req_port_o.data_we = 1'b0; ptw_error_o = 1'b0; ptw_access_exception_o = 1'b0; From 891f9e86e7b30c3cf1576858219fb8cbdb8606c1 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 17:45:29 +0100 Subject: [PATCH 21/64] top mmu with no shared tlb, common top with no common exceptions --- core/mmu_unify/cva6_mmu.sv | 1148 +++++++++++++++++++----------------- 1 file changed, 606 insertions(+), 542 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 59658a6a40..3f9d2164f1 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Thales. +// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -8,262 +8,307 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Sebastien Jacq Thales Research & Technology -// Date: 17/07/2021 -// -// Additional contributions by: -// Sebastien Jacq - sjthales on github.com -// -// Description: Memory Management Unit for CV32A6, contains TLB and -// address translation unit. Sv32 as defined in RISC-V -// privilege specification 1.11-WIP. -// This module is an adaptation of the MMU Sv39 developed -// by Florian Zaruba to the Sv32 standard. -// -// =========================================================================== // -// Revisions : -// Date Version Author Description -// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 -// =========================================================================== // +// Author: Florian Zaruba, ETH Zurich +// Date: 19/04/2017 +// Description: Memory Management Unit for Ariane, contains TLB and +// address translation unit. SV39 as defined in RISC-V +// privilege specification 1.11-WIP + module cva6_mmu - import ariane_pkg::*; +import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 2, - parameter int unsigned DATA_TLB_ENTRIES = 2, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i +); + +logic iaccess_err; // insufficient privilege to access this instruction page +logic daccess_err; // insufficient privilege to access this data page +logic ptw_active; // PTW is currently walking a page table +logic walking_instr; // PTW is walking because of an ITLB miss +logic ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + +logic [riscv::VLEN-1:0] update_vaddr; +tlb_update_t update_ptw_itlb, update_ptw_dtlb; +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +riscv::pte_cva6_t itlb_content; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; + +logic dtlb_lu_access; +riscv::pte_cva6_t dtlb_content; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic dtlb_lu_hit; + +logic shared_tlb_access; +logic [riscv::VLEN-1:0] shared_tlb_vaddr; +logic shared_tlb_hit; + + +// Assignments +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + +// cva6_shared_tlb #( +// .CVA6Cfg (CVA6Cfg), +// .SHARED_TLB_DEPTH(64), +// .SHARED_TLB_WAYS (2), +// .ASID_WIDTH (ASID_WIDTH), +// .ASID_LEN (ASID_LEN), +// .VPN_LEN(VPN_LEN), +// .PT_LEVELS(PT_LEVELS) +// ) i_shared_tlb ( +// .clk_i (clk_i), +// .rst_ni (rst_ni), +// .flush_i(flush_tlb_i), + +// .enable_translation_i (enable_translation_i), +// .en_ld_st_translation_i(en_ld_st_translation_i), + +// .asid_i (asid_i), +// // from TLBs +// // did we miss? +// .itlb_access_i(itlb_lu_access), +// .itlb_hit_i (itlb_lu_hit), +// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + +// .dtlb_access_i(dtlb_lu_access), +// .dtlb_hit_i (dtlb_lu_hit), +// .dtlb_vaddr_i (lsu_vaddr_i), + +// // to TLBs, update logic +// .itlb_update_o(update_itlb), +// .dtlb_update_o(update_dtlb), + +// // Performance counters +// .itlb_miss_o(itlb_miss_o), +// .dtlb_miss_o(dtlb_miss_o), + +// .shared_tlb_access_o(shared_tlb_access), +// .shared_tlb_hit_o (shared_tlb_hit), +// .shared_tlb_vaddr_o (shared_tlb_vaddr), + +// .itlb_req_o (itlb_req), +// // to update shared tlb +// .shared_tlb_update_i(update_shared_tlb) +// ); + +// cva6_ptw #( +// .CVA6Cfg (CVA6Cfg), +// .ASID_WIDTH(ASID_WIDTH), +// .VPN_LEN(VPN_LEN), +// .PT_LEVELS(PT_LEVELS) +// ) i_ptw ( +// .clk_i (clk_i), +// .rst_ni (rst_ni), +// .flush_i(flush_i), + +// .ptw_active_o (ptw_active), +// .walking_instr_o (walking_instr), +// .ptw_error_o (ptw_error), +// .ptw_access_exception_o(ptw_access_exception), + +// .lsu_is_store_i(lsu_is_store_i), +// // PTW memory interface +// .req_port_i (req_port_i), +// .req_port_o (req_port_o), + +// // to Shared TLB, update logic +// .shared_tlb_update_o(update_shared_tlb), + +// .update_vaddr_o(update_vaddr), + +// .asid_i(asid_i), + +// // from shared TLB +// // did we miss? +// .shared_tlb_access_i(shared_tlb_access), +// .shared_tlb_hit_i (shared_tlb_hit), +// .shared_tlb_vaddr_i (shared_tlb_vaddr), + +// .itlb_req_i(itlb_req), + +// // from CSR file +// .satp_ppn_i(satp_ppn_i), // ppn from satp +// .mxr_i (mxr_i), + +// // Performance counters +// .shared_tlb_miss_o(), //open for now + +// // PMP +// .pmpcfg_i (pmpcfg_i), +// .pmpaddr_i (pmpaddr_i), +// .bad_paddr_o(ptw_bad_paddr) + +// ); + +ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH) +) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + .enable_translation_i (enable_translation_i), + + .update_vaddr_o(update_vaddr), + .itlb_update_o (update_ptw_itlb), + .dtlb_update_o (update_ptw_dtlb), + + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + .req_port_i (req_port_i), + .req_port_o (req_port_o), + .pmpcfg_i, + .pmpaddr_i, + .bad_paddr_o(ptw_bad_paddr), + .* ); - logic iaccess_err; // insufficient privilege to access this instruction page - logic daccess_err; // insufficient privilege to access this data page - logic ptw_active; // PTW is currently walking a page table - logic walking_instr; // PTW is walking because of an ITLB miss - logic ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - - logic [riscv::VLEN-1:0] update_vaddr; - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; - - logic itlb_lu_access; - riscv::pte_cva6_t itlb_content; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - - logic dtlb_lu_access; - riscv::pte_cva6_t dtlb_content; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic dtlb_lu_hit; - - logic shared_tlb_access; - logic [riscv::VLEN-1:0] shared_tlb_vaddr; - logic shared_tlb_hit; - - logic itlb_req; - - - // Assignments - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - cva6_shared_tlb #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), - - // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) - ); - - cva6_ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), - // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now - - // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) - - ); - -// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; -// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; -// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; -// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; -// assign update_shared_tlb.content = update_shared_tlb_sv32.content; +assign update_dtlb.valid = update_ptw_dtlb.valid; +assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; +assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; +assign update_dtlb.vpn = update_ptw_dtlb.vpn; +assign update_dtlb.asid = update_ptw_dtlb.asid; +assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; +assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; +assign update_dtlb.content.d = update_ptw_dtlb.content.d; +assign update_dtlb.content.a = update_ptw_dtlb.content.a; +assign update_dtlb.content.g = update_ptw_dtlb.content.g; +assign update_dtlb.content.u = update_ptw_dtlb.content.u; +assign update_dtlb.content.x = update_ptw_dtlb.content.x; +assign update_dtlb.content.w = update_ptw_dtlb.content.w; +assign update_dtlb.content.r = update_ptw_dtlb.content.r; +assign update_dtlb.content.v = update_ptw_dtlb.content.v; + +assign update_itlb.valid = update_ptw_itlb.valid; +assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; +assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; +assign update_itlb.vpn = update_ptw_itlb.vpn; +assign update_itlb.asid = update_ptw_itlb.asid; +assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; +assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; +assign update_itlb.content.d = update_ptw_itlb.content.d; +assign update_itlb.content.a = update_ptw_itlb.content.a; +assign update_itlb.content.g = update_ptw_itlb.content.g; +assign update_itlb.content.u = update_ptw_itlb.content.u; +assign update_itlb.content.x = update_ptw_itlb.content.x; +assign update_itlb.content.w = update_ptw_itlb.content.w; +assign update_itlb.content.r = update_ptw_itlb.content.r; +assign update_itlb.content.v = update_ptw_itlb.content.v; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -274,8 +319,8 @@ module cva6_mmu // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 // .probe5(ptw_error), // input wire [1:0] probe5 // .probe6(update_vaddr), // input wire [0:0] probe6 -// .probe7(update_itlb.valid), // input wire [0:0] probe7 -// .probe8(update_dtlb.valid), // input wire [0:0] probe8 +// .probe7(update_ptw_itlb.valid), // input wire [0:0] probe7 +// .probe8(update_ptw_dtlb.valid), // input wire [0:0] probe8 // .probe9(dtlb_lu_access), // input wire [0:0] probe9 // .probe10(lsu_vaddr_i), // input wire [0:0] probe10 // .probe11(dtlb_lu_hit), // input wire [0:0] probe11 @@ -287,318 +332,337 @@ module cva6_mmu //----------------------- // Instruction Interface //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + +assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; +assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar a; +generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // - (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // - itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // - icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; - end +endgenerate + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - endgenerate - - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end - - icache_areq_o.fetch_valid = 1'b0; - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //to check on wave --> not connected + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; end end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + 1'b1 + }; + end +end - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; - +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; - - assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; + + +assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + + endgenerate // The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; end - endgenerate - - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end end - end - - // Load/store PMP check - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end end +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end endmodule \ No newline at end of file From 888e49fdd57be9b2c3c16183a1759956fce6485a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 18:16:33 +0100 Subject: [PATCH 22/64] parameterize top no exceptions based on sv39 with no shared tlb --- core/mmu_unify/cva6_mmu.sv | 140 +++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 54 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 3f9d2164f1..aa9c72834c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,35 +329,36 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- +//----------------------- +// Instruction Interface +//----------------------- logic match_any_execute_region; logic pmp_instr_allow; localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; -assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; -assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar a; -generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end + assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; + assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); -endgenerate + genvar a; + generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end + + endgenerate // The instruction interface is a simple request response interface always_comb begin : instr_interface // MMU disabled: just pass through icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation // two potential exception 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 @@ -383,6 +384,17 @@ always_comb begin : instr_interface icache_areq_o.fetch_valid = 1'b0; + // // 4K page + // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // // Mega page + // if (itlb_is_page[1]) begin + // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; + // end + // // Giga page + // if (itlb_is_page[0]) begin + // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; + // end + // --------- // ITLB Hit // -------- @@ -458,12 +470,13 @@ pmp #( // Data Interface //----------------------- logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_t dtlb_pte_n, dtlb_pte_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; +logic dtlb_is_2M_n, dtlb_is_2M_q; +logic dtlb_is_1G_n, dtlb_is_1G_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -471,35 +484,36 @@ assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; logic pmp_data_allow; +// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end - -assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; -assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; - end - - endgenerate // The data interface is simpler and only consists of a request/response interface + endgenerate +// The data interface is simpler and only consists of a request/response interface always_comb begin : data_interface // save request and DTLB response lsu_vaddr_n = lsu_vaddr_i; @@ -508,7 +522,11 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + dtlb_is_2M_n = dtlb_is_page[1]; + dtlb_is_1G_n = dtlb_is_page[0]; + + // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; + // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; lsu_valid_o = lsu_req_q; lsu_exception_o = misaligned_ex_q; pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; @@ -523,7 +541,19 @@ always_comb begin : data_interface // translation is enabled and no misaligned exception occurred if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; - + // // 4K page + // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; + // lsu_dtlb_ppn_o = dtlb_content.ppn; + // // Mega page + // if (dtlb_is_2M_q) begin + // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; + // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; + // end + // // Giga page + // if (dtlb_is_1G_q) begin + // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; + // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; + // end // --------- // DTLB Hit // -------- @@ -654,7 +684,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + dtlb_is_2M_q <= '0; + dtlb_is_1G_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -662,7 +693,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + dtlb_is_2M_q <= dtlb_is_2M_n; + dtlb_is_1G_q <= dtlb_is_1G_n; end end -endmodule \ No newline at end of file +endmodule From 2a10823a34fe08ac2266e55eb77d88c88fd37cbe Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 18:40:09 +0100 Subject: [PATCH 23/64] change all mmu to "is_page" concept --- core/mmu_unify/cva6_mmu.sv | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index aa9c72834c..d5dac19c4e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -475,8 +475,7 @@ exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic dtlb_is_2M_n, dtlb_is_2M_q; -logic dtlb_is_1G_n, dtlb_is_1G_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -522,8 +521,7 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_2M_n = dtlb_is_page[1]; - dtlb_is_1G_n = dtlb_is_page[0]; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; @@ -684,8 +682,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_2M_q <= '0; - dtlb_is_1G_q <= '0; + dtlb_is_page_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -693,8 +690,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_2M_q <= dtlb_is_2M_n; - dtlb_is_1G_q <= dtlb_is_1G_n; + dtlb_is_page_q <= dtlb_is_page_n; end end endmodule From bcbe49b8df1ba0a2cc55d7528edb21302ac4c667 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 6 Dec 2023 19:57:04 +0100 Subject: [PATCH 24/64] add common exceptions in top --- core/mmu_unify/cva6_mmu.sv | 526 ++++++++++++++++++------------------- 1 file changed, 253 insertions(+), 273 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d5dac19c4e..e4f0a2f7f7 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,12 +329,12 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); -//----------------------- -// Instruction Interface -//----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + //----------------------- + // Instruction Interface + //----------------------- + logic match_any_execute_region; + logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -354,35 +354,35 @@ localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; endgenerate -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - icache_areq_o.fetch_valid = 1'b0; + icache_areq_o.fetch_valid = 1'b0; // // 4K page // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; @@ -395,94 +395,90 @@ always_comb begin : instr_interface // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; // end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; end end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, - 1'b1 - }; - end -end -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); - -// Instruction fetch -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); - -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); + + // Instruction fetch + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; // localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; @@ -512,33 +508,33 @@ logic pmp_data_allow; end endgenerate -// The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; // // 4K page // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; // lsu_dtlb_ppn_o = dtlb_content.ppn; @@ -552,145 +548,129 @@ always_comb begin : data_interface // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin lsu_valid_o = 1'b1; - // the page table walker can only throw page faults + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end + + // this is a load end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end end - end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end end end -end -// Load/store PMP check -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); - -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + // Load/store PMP check + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); + + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end -endmodule +endmodule \ No newline at end of file From 53125fbd911a3712efcb319a51a144cca22f1b80 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:05:53 +0100 Subject: [PATCH 25/64] Revert "add common exceptions in top" This reverts commit bcbe49b8df1ba0a2cc55d7528edb21302ac4c667. --- core/mmu_unify/cva6_mmu.sv | 526 +++++++++++++++++++------------------ 1 file changed, 273 insertions(+), 253 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e4f0a2f7f7..d5dac19c4e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,12 +329,12 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -354,35 +354,35 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; endgenerate - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - icache_areq_o.fetch_valid = 1'b0; + icache_areq_o.fetch_valid = 1'b0; // // 4K page // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; @@ -395,90 +395,94 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; // end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + 1'b1 + }; + end +end - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); + +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; // localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; @@ -508,33 +512,33 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; end endgenerate - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; // // 4K page // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; // lsu_dtlb_ppn_o = dtlb_content.ppn; @@ -548,129 +552,145 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end - // this is a store + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end end - end - - // Load/store PMP check - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end end -endmodule \ No newline at end of file +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end +endmodule From 724227e3db73b4a88863a3da07369a11048a3e56 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:06:38 +0100 Subject: [PATCH 26/64] Revert "Revert "add common exceptions in top"" This reverts commit 53125fbd911a3712efcb319a51a144cca22f1b80. --- core/mmu_unify/cva6_mmu.sv | 526 ++++++++++++++++++------------------- 1 file changed, 253 insertions(+), 273 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d5dac19c4e..e4f0a2f7f7 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,12 +329,12 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); -//----------------------- -// Instruction Interface -//----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + //----------------------- + // Instruction Interface + //----------------------- + logic match_any_execute_region; + logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -354,35 +354,35 @@ localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; endgenerate -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - icache_areq_o.fetch_valid = 1'b0; + icache_areq_o.fetch_valid = 1'b0; // // 4K page // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; @@ -395,94 +395,90 @@ always_comb begin : instr_interface // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; // end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; end end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, - 1'b1 - }; - end -end -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); - -// Instruction fetch -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); - -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); + + // Instruction fetch + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; // localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; @@ -512,33 +508,33 @@ logic pmp_data_allow; end endgenerate -// The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; // // 4K page // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; // lsu_dtlb_ppn_o = dtlb_content.ppn; @@ -552,145 +548,129 @@ always_comb begin : data_interface // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin lsu_valid_o = 1'b1; - // the page table walker can only throw page faults + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end + + // this is a load end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end end - end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end end end -end -// Load/store PMP check -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); - -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + // Load/store PMP check + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); + + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end -endmodule +endmodule \ No newline at end of file From 9ffe0524413e5f50ada744ba08d153fdede32bbe Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:08:19 +0100 Subject: [PATCH 27/64] Revert "correct req_port_o.data_size in ptw" This reverts commit ace52fec7366b3f4447cd45958f8a31c002bfa30. --- core/mmu_unify/cva6_ptw.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index fb24401f42..15c28a6f23 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -215,7 +215,7 @@ genvar x; // PTW memory interface tag_valid_n = 1'b0; req_port_o.data_req = 1'b0; - req_port_o.data_size = 2'(PT_LEVELS); + req_port_o.data_size = 2'b{PT_LEVELS}; req_port_o.data_we = 1'b0; ptw_error_o = 1'b0; ptw_access_exception_o = 1'b0; From 11573d2a9781573f05d9a22471f25e770094d978 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:09:30 +0100 Subject: [PATCH 28/64] Revert "Revert "correct req_port_o.data_size in ptw"" This reverts commit 9ffe0524413e5f50ada744ba08d153fdede32bbe. --- core/mmu_unify/cva6_ptw.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 15c28a6f23..fb24401f42 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -215,7 +215,7 @@ genvar x; // PTW memory interface tag_valid_n = 1'b0; req_port_o.data_req = 1'b0; - req_port_o.data_size = 2'b{PT_LEVELS}; + req_port_o.data_size = 2'(PT_LEVELS); req_port_o.data_we = 1'b0; ptw_error_o = 1'b0; ptw_access_exception_o = 1'b0; From 2a9c97b86635cf1c58a0fa3cf806e4c1aa8b8194 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:11:23 +0100 Subject: [PATCH 29/64] Revert "add common exceptions in top" This reverts commit bcbe49b8df1ba0a2cc55d7528edb21302ac4c667. --- core/mmu_unify/cva6_mmu.sv | 526 +++++++++++++++++++------------------ 1 file changed, 273 insertions(+), 253 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e4f0a2f7f7..d5dac19c4e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,12 +329,12 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -354,35 +354,35 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; endgenerate - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - icache_areq_o.fetch_valid = 1'b0; + icache_areq_o.fetch_valid = 1'b0; // // 4K page // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; @@ -395,90 +395,94 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; // end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + 1'b1 + }; + end +end - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); + +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; // localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; @@ -508,33 +512,33 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; end endgenerate - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; // // 4K page // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; // lsu_dtlb_ppn_o = dtlb_content.ppn; @@ -548,129 +552,145 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end - // this is a store + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end end - end - - // Load/store PMP check - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end end -endmodule \ No newline at end of file +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end +endmodule From 669208d2e1e60d7d0cdbd2d86133f3b00f49c5c7 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:12:31 +0100 Subject: [PATCH 30/64] Revert "change all mmu to "is_page" concept" This reverts commit 2a10823a34fe08ac2266e55eb77d88c88fd37cbe. --- core/mmu_unify/cva6_mmu.sv | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d5dac19c4e..aa9c72834c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -475,7 +475,8 @@ exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; +logic dtlb_is_2M_n, dtlb_is_2M_q; +logic dtlb_is_1G_n, dtlb_is_1G_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -521,7 +522,8 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + dtlb_is_2M_n = dtlb_is_page[1]; + dtlb_is_1G_n = dtlb_is_page[0]; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; @@ -682,7 +684,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + dtlb_is_2M_q <= '0; + dtlb_is_1G_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -690,7 +693,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + dtlb_is_2M_q <= dtlb_is_2M_n; + dtlb_is_1G_q <= dtlb_is_1G_n; end end endmodule From 23f5b10a3a1b8bf8be92bfc8d088cbdd5704940f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:12:54 +0100 Subject: [PATCH 31/64] Revert "parameterize top no exceptions based on sv39 with no shared tlb" This reverts commit 888e49fdd57be9b2c3c16183a1759956fce6485a. --- core/mmu_unify/cva6_mmu.sv | 140 ++++++++++++++----------------------- 1 file changed, 54 insertions(+), 86 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index aa9c72834c..3f9d2164f1 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,36 +329,35 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); -//----------------------- -// Instruction Interface -//----------------------- + //----------------------- + // Instruction Interface + //----------------------- logic match_any_execute_region; logic pmp_instr_allow; localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end +assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; +assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar a; +generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end - endgenerate +endgenerate // The instruction interface is a simple request response interface always_comb begin : instr_interface // MMU disabled: just pass through icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation // two potential exception 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 @@ -384,17 +383,6 @@ always_comb begin : instr_interface icache_areq_o.fetch_valid = 1'b0; - // // 4K page - // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_page[1]) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_page[0]) begin - // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; - // end - // --------- // ITLB Hit // -------- @@ -470,13 +458,12 @@ pmp #( // Data Interface //----------------------- logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +riscv::pte_t dtlb_pte_n, dtlb_pte_q; exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic dtlb_is_2M_n, dtlb_is_2M_q; -logic dtlb_is_1G_n, dtlb_is_1G_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -484,36 +471,35 @@ assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; logic pmp_data_allow; -// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; - end - endgenerate -// The data interface is simpler and only consists of a request/response interface + +assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + + endgenerate // The data interface is simpler and only consists of a request/response interface always_comb begin : data_interface // save request and DTLB response lsu_vaddr_n = lsu_vaddr_i; @@ -522,11 +508,7 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_2M_n = dtlb_is_page[1]; - dtlb_is_1G_n = dtlb_is_page[0]; - - // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; - // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; + dtlb_is_page_n = dtlb_is_page; lsu_valid_o = lsu_req_q; lsu_exception_o = misaligned_ex_q; pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; @@ -541,19 +523,7 @@ always_comb begin : data_interface // translation is enabled and no misaligned exception occurred if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; - // // 4K page - // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - // lsu_dtlb_ppn_o = dtlb_content.ppn; - // // Mega page - // if (dtlb_is_2M_q) begin - // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; - // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; - // end - // // Giga page - // if (dtlb_is_1G_q) begin - // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; - // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; - // end + // --------- // DTLB Hit // -------- @@ -684,8 +654,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_2M_q <= '0; - dtlb_is_1G_q <= '0; + dtlb_is_page_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -693,8 +662,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_2M_q <= dtlb_is_2M_n; - dtlb_is_1G_q <= dtlb_is_1G_n; + dtlb_is_page_q <= dtlb_is_page_n; end end -endmodule +endmodule \ No newline at end of file From db49cf4f75b6ead5a63cf996220a5e9b3ac0d4ff Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:13:14 +0100 Subject: [PATCH 32/64] Revert "top mmu with no shared tlb, common top with no common exceptions" This reverts commit 891f9e86e7b30c3cf1576858219fb8cbdb8606c1. --- core/mmu_unify/cva6_mmu.sv | 1148 +++++++++++++++++------------------- 1 file changed, 542 insertions(+), 606 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 3f9d2164f1..59658a6a40 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,4 +1,4 @@ -// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright (c) 2021 Thales. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -8,307 +8,262 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Florian Zaruba, ETH Zurich -// Date: 19/04/2017 -// Description: Memory Management Unit for Ariane, contains TLB and -// address translation unit. SV39 as defined in RISC-V -// privilege specification 1.11-WIP - +// Author: Sebastien Jacq Thales Research & Technology +// Date: 17/07/2021 +// +// Additional contributions by: +// Sebastien Jacq - sjthales on github.com +// +// Description: Memory Management Unit for CV32A6, contains TLB and +// address translation unit. Sv32 as defined in RISC-V +// privilege specification 1.11-WIP. +// This module is an adaptation of the MMU Sv39 developed +// by Florian Zaruba to the Sv32 standard. +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 +// =========================================================================== // module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i -); - -logic iaccess_err; // insufficient privilege to access this instruction page -logic daccess_err; // insufficient privilege to access this data page -logic ptw_active; // PTW is currently walking a page table -logic walking_instr; // PTW is walking because of an ITLB miss -logic ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - -logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_t update_ptw_itlb, update_ptw_dtlb; -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - -logic itlb_lu_access; -riscv::pte_cva6_t itlb_content; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; - -logic dtlb_lu_access; -riscv::pte_cva6_t dtlb_content; -logic [PT_LEVELS-2:0] dtlb_is_page; -logic dtlb_lu_hit; - -logic shared_tlb_access; -logic [riscv::VLEN-1:0] shared_tlb_vaddr; -logic shared_tlb_hit; - - -// Assignments -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); - -// cva6_shared_tlb #( -// .CVA6Cfg (CVA6Cfg), -// .SHARED_TLB_DEPTH(64), -// .SHARED_TLB_WAYS (2), -// .ASID_WIDTH (ASID_WIDTH), -// .ASID_LEN (ASID_LEN), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_shared_tlb ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_tlb_i), - -// .enable_translation_i (enable_translation_i), -// .en_ld_st_translation_i(en_ld_st_translation_i), - -// .asid_i (asid_i), -// // from TLBs -// // did we miss? -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// // to TLBs, update logic -// .itlb_update_o(update_itlb), -// .dtlb_update_o(update_dtlb), - -// // Performance counters -// .itlb_miss_o(itlb_miss_o), -// .dtlb_miss_o(dtlb_miss_o), - -// .shared_tlb_access_o(shared_tlb_access), -// .shared_tlb_hit_o (shared_tlb_hit), -// .shared_tlb_vaddr_o (shared_tlb_vaddr), - -// .itlb_req_o (itlb_req), -// // to update shared tlb -// .shared_tlb_update_i(update_shared_tlb) -// ); - -// cva6_ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_i), - -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), - -// .lsu_is_store_i(lsu_is_store_i), -// // PTW memory interface -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), - -// // to Shared TLB, update logic -// .shared_tlb_update_o(update_shared_tlb), - -// .update_vaddr_o(update_vaddr), - -// .asid_i(asid_i), - -// // from shared TLB -// // did we miss? -// .shared_tlb_access_i(shared_tlb_access), -// .shared_tlb_hit_i (shared_tlb_hit), -// .shared_tlb_vaddr_i (shared_tlb_vaddr), - -// .itlb_req_i(itlb_req), - -// // from CSR file -// .satp_ppn_i(satp_ppn_i), // ppn from satp -// .mxr_i (mxr_i), - -// // Performance counters -// .shared_tlb_miss_o(), //open for now - -// // PMP -// .pmpcfg_i (pmpcfg_i), -// .pmpaddr_i (pmpaddr_i), -// .bad_paddr_o(ptw_bad_paddr) - -// ); - -ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH) -) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - .enable_translation_i (enable_translation_i), - - .update_vaddr_o(update_vaddr), - .itlb_update_o (update_ptw_itlb), - .dtlb_update_o (update_ptw_dtlb), - - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - .req_port_i (req_port_i), - .req_port_o (req_port_o), - .pmpcfg_i, - .pmpaddr_i, - .bad_paddr_o(ptw_bad_paddr), - .* + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -assign update_dtlb.valid = update_ptw_dtlb.valid; -assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -assign update_dtlb.vpn = update_ptw_dtlb.vpn; -assign update_dtlb.asid = update_ptw_dtlb.asid; -assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -assign update_dtlb.content.d = update_ptw_dtlb.content.d; -assign update_dtlb.content.a = update_ptw_dtlb.content.a; -assign update_dtlb.content.g = update_ptw_dtlb.content.g; -assign update_dtlb.content.u = update_ptw_dtlb.content.u; -assign update_dtlb.content.x = update_ptw_dtlb.content.x; -assign update_dtlb.content.w = update_ptw_dtlb.content.w; -assign update_dtlb.content.r = update_ptw_dtlb.content.r; -assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -assign update_itlb.valid = update_ptw_itlb.valid; -assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -assign update_itlb.vpn = update_ptw_itlb.vpn; -assign update_itlb.asid = update_ptw_itlb.asid; -assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -assign update_itlb.content.d = update_ptw_itlb.content.d; -assign update_itlb.content.a = update_ptw_itlb.content.a; -assign update_itlb.content.g = update_ptw_itlb.content.g; -assign update_itlb.content.u = update_ptw_itlb.content.u; -assign update_itlb.content.x = update_ptw_itlb.content.x; -assign update_itlb.content.w = update_ptw_itlb.content.w; -assign update_itlb.content.r = update_ptw_itlb.content.r; -assign update_itlb.content.v = update_ptw_itlb.content.v; + logic iaccess_err; // insufficient privilege to access this instruction page + logic daccess_err; // insufficient privilege to access this data page + logic ptw_active; // PTW is currently walking a page table + logic walking_instr; // PTW is walking because of an ITLB miss + logic ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + + logic [riscv::VLEN-1:0] update_vaddr; + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; + + logic itlb_lu_access; + riscv::pte_cva6_t itlb_content; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + + logic dtlb_lu_access; + riscv::pte_cva6_t dtlb_content; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic dtlb_lu_hit; + + logic shared_tlb_access; + logic [riscv::VLEN-1:0] shared_tlb_vaddr; + logic shared_tlb_hit; + + logic itlb_req; + + + // Assignments + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) + ); + + cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + + ); + +// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; +// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; +// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; +// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; +// assign update_shared_tlb.content = update_shared_tlb_sv32.content; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -319,8 +274,8 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 // .probe5(ptw_error), // input wire [1:0] probe5 // .probe6(update_vaddr), // input wire [0:0] probe6 -// .probe7(update_ptw_itlb.valid), // input wire [0:0] probe7 -// .probe8(update_ptw_dtlb.valid), // input wire [0:0] probe8 +// .probe7(update_itlb.valid), // input wire [0:0] probe7 +// .probe8(update_dtlb.valid), // input wire [0:0] probe8 // .probe9(dtlb_lu_access), // input wire [0:0] probe9 // .probe10(lsu_vaddr_i), // input wire [0:0] probe10 // .probe11(dtlb_lu_hit), // input wire [0:0] probe11 @@ -332,337 +287,318 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; //----------------------- // Instruction Interface //----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - -assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; -assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar a; -generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end + logic match_any_execute_region; + logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; -endgenerate - -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; + assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - icache_areq_o.fetch_valid = 1'b0; - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin + genvar a; + generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // + (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // + itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // + icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; + end + + endgenerate + + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, - 1'b1 - }; - end -end - -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); - -// Instruction fetch -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); - -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; - - -assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; -assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; - end - endgenerate // The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //to check on wave --> not connected + end + end + + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); + + // Instruction fetch + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; + + + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + + + genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + + assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // + dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // + lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; end - end - if (ptw_access_exception) begin - // an error makes the translation valid + endgenerate + + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + + // this is a load end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; //only 32 bits on 34b of lsu_paddr_o are returned. + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end end end -end - -// Load/store PMP check -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + // Load/store PMP check + pmp #( + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); + + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end endmodule \ No newline at end of file From c1f50d8fe35495ddcad441d5de74e208e66c8781 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:24:25 +0100 Subject: [PATCH 33/64] common top sv39 with shared tlb --- core/mmu_unify/cva6_mmu.sv | 595 +++++++++++++++++++------------------ 1 file changed, 298 insertions(+), 297 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e4f0a2f7f7..50079f3173 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,4 +1,4 @@ -// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright (c) 2021 Thales. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -8,307 +8,309 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Florian Zaruba, ETH Zurich -// Date: 19/04/2017 -// Description: Memory Management Unit for Ariane, contains TLB and -// address translation unit. SV39 as defined in RISC-V -// privilege specification 1.11-WIP +// Author: Angela Gonzalez, PlanV Technology +// Date: 07/12/2023 +// Description: Memory Management Unit for CVA6, contains TLB and +// address translation unit. SV32 and SV39 as defined in RISC-V +// privilege specification 1.11-WIP. +// This module is an merge of the MMU Sv39 developed +// by Florian Zaruba and the MMU Sv32 developed by Sebastien Jacq. module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -logic iaccess_err; // insufficient privilege to access this instruction page -logic daccess_err; // insufficient privilege to access this data page -logic ptw_active; // PTW is currently walking a page table -logic walking_instr; // PTW is walking because of an ITLB miss -logic ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - -logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_t update_ptw_itlb, update_ptw_dtlb; -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - -logic itlb_lu_access; -riscv::pte_cva6_t itlb_content; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; - -logic dtlb_lu_access; -riscv::pte_cva6_t dtlb_content; -logic [PT_LEVELS-2:0] dtlb_is_page; -logic dtlb_lu_hit; - -logic shared_tlb_access; -logic [riscv::VLEN-1:0] shared_tlb_vaddr; -logic shared_tlb_hit; - - -// Assignments -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); + logic iaccess_err; // insufficient privilege to access this instruction page + logic daccess_err; // insufficient privilege to access this data page + logic ptw_active; // PTW is currently walking a page table + logic walking_instr; // PTW is walking because of an ITLB miss + logic ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + + logic [riscv::VLEN-1:0] update_vaddr; + // tlb_update_t update_ptw_itlb, update_ptw_dtlb; + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + riscv::pte_cva6_t itlb_content; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + + logic dtlb_lu_access; + riscv::pte_cva6_t dtlb_content; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic dtlb_lu_hit; + + logic shared_tlb_access; + logic [riscv::VLEN-1:0] shared_tlb_vaddr; + logic shared_tlb_hit; + + + // Assignments + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); -cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) -) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); + cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); -// cva6_shared_tlb #( -// .CVA6Cfg (CVA6Cfg), -// .SHARED_TLB_DEPTH(64), -// .SHARED_TLB_WAYS (2), -// .ASID_WIDTH (ASID_WIDTH), -// .ASID_LEN (ASID_LEN), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_shared_tlb ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_tlb_i), - -// .enable_translation_i (enable_translation_i), -// .en_ld_st_translation_i(en_ld_st_translation_i), - -// .asid_i (asid_i), -// // from TLBs -// // did we miss? -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// // to TLBs, update logic -// .itlb_update_o(update_itlb), -// .dtlb_update_o(update_dtlb), - -// // Performance counters -// .itlb_miss_o(itlb_miss_o), -// .dtlb_miss_o(dtlb_miss_o), - -// .shared_tlb_access_o(shared_tlb_access), -// .shared_tlb_hit_o (shared_tlb_hit), -// .shared_tlb_vaddr_o (shared_tlb_vaddr), - -// .itlb_req_o (itlb_req), -// // to update shared tlb -// .shared_tlb_update_i(update_shared_tlb) -// ); - -// cva6_ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_i), - -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), - -// .lsu_is_store_i(lsu_is_store_i), -// // PTW memory interface -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), - -// // to Shared TLB, update logic -// .shared_tlb_update_o(update_shared_tlb), - -// .update_vaddr_o(update_vaddr), - -// .asid_i(asid_i), - -// // from shared TLB -// // did we miss? -// .shared_tlb_access_i(shared_tlb_access), -// .shared_tlb_hit_i (shared_tlb_hit), -// .shared_tlb_vaddr_i (shared_tlb_vaddr), - -// .itlb_req_i(itlb_req), - -// // from CSR file -// .satp_ppn_i(satp_ppn_i), // ppn from satp -// .mxr_i (mxr_i), - -// // Performance counters -// .shared_tlb_miss_o(), //open for now - -// // PMP -// .pmpcfg_i (pmpcfg_i), -// .pmpaddr_i (pmpaddr_i), -// .bad_paddr_o(ptw_bad_paddr) - -// ); - -ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH) -) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - .enable_translation_i (enable_translation_i), - - .update_vaddr_o(update_vaddr), - .itlb_update_o (update_ptw_itlb), - .dtlb_update_o (update_ptw_dtlb), - - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - .req_port_i (req_port_i), - .req_port_o (req_port_o), - .pmpcfg_i, - .pmpaddr_i, - .bad_paddr_o(ptw_bad_paddr), - .* -); + cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) + ); + + cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) + ) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + + ); + +// ptw #( +// .CVA6Cfg (CVA6Cfg), +// .ASID_WIDTH(ASID_WIDTH) +// ) i_ptw ( +// .clk_i (clk_i), +// .rst_ni (rst_ni), +// .ptw_active_o (ptw_active), +// .walking_instr_o (walking_instr), +// .ptw_error_o (ptw_error), +// .ptw_access_exception_o(ptw_access_exception), +// .enable_translation_i (enable_translation_i), + +// .update_vaddr_o(update_vaddr), +// .itlb_update_o (update_ptw_itlb), +// .dtlb_update_o (update_ptw_dtlb), + +// .itlb_access_i(itlb_lu_access), +// .itlb_hit_i (itlb_lu_hit), +// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + +// .dtlb_access_i(dtlb_lu_access), +// .dtlb_hit_i (dtlb_lu_hit), +// .dtlb_vaddr_i (lsu_vaddr_i), + +// .req_port_i (req_port_i), +// .req_port_o (req_port_o), +// .pmpcfg_i, +// .pmpaddr_i, +// .bad_paddr_o(ptw_bad_paddr), +// .* +// ); -assign update_dtlb.valid = update_ptw_dtlb.valid; -assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -assign update_dtlb.vpn = update_ptw_dtlb.vpn; -assign update_dtlb.asid = update_ptw_dtlb.asid; -assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -assign update_dtlb.content.d = update_ptw_dtlb.content.d; -assign update_dtlb.content.a = update_ptw_dtlb.content.a; -assign update_dtlb.content.g = update_ptw_dtlb.content.g; -assign update_dtlb.content.u = update_ptw_dtlb.content.u; -assign update_dtlb.content.x = update_ptw_dtlb.content.x; -assign update_dtlb.content.w = update_ptw_dtlb.content.w; -assign update_dtlb.content.r = update_ptw_dtlb.content.r; -assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -assign update_itlb.valid = update_ptw_itlb.valid; -assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -assign update_itlb.vpn = update_ptw_itlb.vpn; -assign update_itlb.asid = update_ptw_itlb.asid; -assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -assign update_itlb.content.d = update_ptw_itlb.content.d; -assign update_itlb.content.a = update_ptw_itlb.content.a; -assign update_itlb.content.g = update_ptw_itlb.content.g; -assign update_itlb.content.u = update_ptw_itlb.content.u; -assign update_itlb.content.x = update_ptw_itlb.content.x; -assign update_itlb.content.w = update_ptw_itlb.content.w; -assign update_itlb.content.r = update_ptw_itlb.content.r; -assign update_itlb.content.v = update_ptw_itlb.content.v; +// assign update_dtlb.valid = update_ptw_dtlb.valid; +// assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; +// assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; +// assign update_dtlb.vpn = update_ptw_dtlb.vpn; +// assign update_dtlb.asid = update_ptw_dtlb.asid; +// assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; +// assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; +// assign update_dtlb.content.d = update_ptw_dtlb.content.d; +// assign update_dtlb.content.a = update_ptw_dtlb.content.a; +// assign update_dtlb.content.g = update_ptw_dtlb.content.g; +// assign update_dtlb.content.u = update_ptw_dtlb.content.u; +// assign update_dtlb.content.x = update_ptw_dtlb.content.x; +// assign update_dtlb.content.w = update_ptw_dtlb.content.w; +// assign update_dtlb.content.r = update_ptw_dtlb.content.r; +// assign update_dtlb.content.v = update_ptw_dtlb.content.v; + +// assign update_itlb.valid = update_ptw_itlb.valid; +// assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; +// assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; +// assign update_itlb.vpn = update_ptw_itlb.vpn; +// assign update_itlb.asid = update_ptw_itlb.asid; +// assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; +// assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; +// assign update_itlb.content.d = update_ptw_itlb.content.d; +// assign update_itlb.content.a = update_ptw_itlb.content.a; +// assign update_itlb.content.g = update_ptw_itlb.content.g; +// assign update_itlb.content.u = update_ptw_itlb.content.u; +// assign update_itlb.content.x = update_ptw_itlb.content.x; +// assign update_itlb.content.w = update_ptw_itlb.content.w; +// assign update_itlb.content.r = update_ptw_itlb.content.r; +// assign update_itlb.content.v = update_ptw_itlb.content.v; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -364,8 +366,7 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // 2. We got an access error because of insufficient permissions -> throw an access exception icache_areq_o.fetch_exception = '0; // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); // MMU enabled: address from TLB, request delayed until hit. Error when TLB @@ -433,7 +434,7 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; end // if it didn't match any execute region throw an `Instruction Access Fault` // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 }; @@ -530,8 +531,8 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // Check if the User flag is set, then we may only access it in supervisor mode // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it // translation is enabled and no misaligned exception occurred if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; From fd36bae0c2b0738fa36f461d39a6e11bd212844e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 10:33:05 +0100 Subject: [PATCH 34/64] correct is_page assignment in shared tlb --- core/mmu_unify/cva6_shared_tlb.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index ba40875997..e74421620d 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -149,8 +149,9 @@ module cva6_shared_tlb generate for (i=0; i < SHARED_TLB_WAYS; i++) begin //identify page_match for all TLB Entries - assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; + // assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; for (x=0; x < PT_LEVELS; x++) begin + assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; // assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; From 882b9dfd099e506ad6a1249280f528d437abae1e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 13:22:53 +0100 Subject: [PATCH 35/64] Revert "Merge branch 'tmp' into mmu_unify" This reverts commit 6a4867d5351b2766137323a84b1bafcb5b12ed19, reversing changes made to db49cf4f75b6ead5a63cf996220a5e9b3ac0d4ff. --- core/mmu_unify/cva6_mmu.sv | 103 ++++++------------------------ core/mmu_unify/cva6_shared_tlb.sv | 3 +- 2 files changed, 19 insertions(+), 87 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6f39d3e737..59658a6a40 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,5 +1,4 @@ // Copyright (c) 2021 Thales. -// Copyright (c) 2021 Thales. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -9,14 +8,6 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Angela Gonzalez, PlanV Technology -// Date: 07/12/2023 -// Description: Memory Management Unit for CVA6, contains TLB and -// address translation unit. SV32 and SV39 as defined in RISC-V -// privilege specification 1.11-WIP. -// This module is an merge of the MMU Sv39 developed -// by Florian Zaruba and the MMU Sv32 developed by Sebastien Jacq. - // Author: Sebastien Jacq Thales Research & Technology // Date: 17/07/2021 // @@ -36,11 +27,11 @@ // =========================================================================== // module cva6_mmu - import ariane_pkg::*; + import ariane_pkg::*; #( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter int unsigned INSTR_TLB_ENTRIES = 2, + parameter int unsigned DATA_TLB_ENTRIES = 2, parameter int unsigned ASID_WIDTH = 1, parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, @@ -91,17 +82,17 @@ module cva6_mmu input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); - logic iaccess_err; // insufficient privilege to access this instruction page - logic daccess_err; // insufficient privilege to access this data page - logic ptw_active; // PTW is currently walking a page table - logic walking_instr; // PTW is walking because of an ITLB miss - logic ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + logic iaccess_err; // insufficient privilege to access this instruction page + logic daccess_err; // insufficient privilege to access this data page + logic ptw_active; // PTW is currently walking a page table + logic walking_instr; // PTW is walking because of an ITLB miss + logic ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr -logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_t update_ptw_itlb, update_ptw_dtlb; -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + logic [riscv::VLEN-1:0] update_vaddr; + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; logic itlb_lu_access; riscv::pte_cva6_t itlb_content; @@ -268,69 +259,11 @@ tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; ); -// ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), -// .enable_translation_i (enable_translation_i), - -// .update_vaddr_o(update_vaddr), -// .itlb_update_o (update_ptw_itlb), -// .dtlb_update_o (update_ptw_dtlb), - -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), -// .pmpcfg_i, -// .pmpaddr_i, -// .bad_paddr_o(ptw_bad_paddr), -// .* -// ); - -// assign update_dtlb.valid = update_ptw_dtlb.valid; -// assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -// assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -// assign update_dtlb.vpn = update_ptw_dtlb.vpn; -// assign update_dtlb.asid = update_ptw_dtlb.asid; -// assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -// assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -// assign update_dtlb.content.d = update_ptw_dtlb.content.d; -// assign update_dtlb.content.a = update_ptw_dtlb.content.a; -// assign update_dtlb.content.g = update_ptw_dtlb.content.g; -// assign update_dtlb.content.u = update_ptw_dtlb.content.u; -// assign update_dtlb.content.x = update_ptw_dtlb.content.x; -// assign update_dtlb.content.w = update_ptw_dtlb.content.w; -// assign update_dtlb.content.r = update_ptw_dtlb.content.r; -// assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -// assign update_itlb.valid = update_ptw_itlb.valid; -// assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -// assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -// assign update_itlb.vpn = update_ptw_itlb.vpn; -// assign update_itlb.asid = update_ptw_itlb.asid; -// assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -// assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -// assign update_itlb.content.d = update_ptw_itlb.content.d; -// assign update_itlb.content.a = update_ptw_itlb.content.a; -// assign update_itlb.content.g = update_ptw_itlb.content.g; -// assign update_itlb.content.u = update_ptw_itlb.content.u; -// assign update_itlb.content.x = update_ptw_itlb.content.x; -// assign update_itlb.content.w = update_ptw_itlb.content.w; -// assign update_itlb.content.r = update_ptw_itlb.content.r; -// assign update_itlb.content.v = update_ptw_itlb.content.v; +// assign update_shared_tlb.valid = update_shared_tlb_sv32.valid; +// assign update_shared_tlb.is_page[0] = update_shared_tlb_sv32.is_4M; +// assign update_shared_tlb.vpn = update_shared_tlb_sv32.vpn; +// assign update_shared_tlb.asid = update_shared_tlb_sv32.asid; +// assign update_shared_tlb.content = update_shared_tlb_sv32.content; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index e74421620d..ba40875997 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -149,9 +149,8 @@ module cva6_shared_tlb generate for (i=0; i < SHARED_TLB_WAYS; i++) begin //identify page_match for all TLB Entries - // assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; + assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; for (x=0; x < PT_LEVELS; x++) begin - assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; // assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; From 6ea3a3422a69b05a84145d355cc24af304fb9638 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 13:32:13 +0100 Subject: [PATCH 36/64] common top clean up --- core/mmu_unify/cva6_mmu.sv | 92 +------------------------------------- 1 file changed, 1 insertion(+), 91 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 50079f3173..6ceb15e366 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -248,69 +248,7 @@ module cva6_mmu ); -// ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), -// .enable_translation_i (enable_translation_i), - -// .update_vaddr_o(update_vaddr), -// .itlb_update_o (update_ptw_itlb), -// .dtlb_update_o (update_ptw_dtlb), - -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), -// .pmpcfg_i, -// .pmpaddr_i, -// .bad_paddr_o(ptw_bad_paddr), -// .* -// ); -// assign update_dtlb.valid = update_ptw_dtlb.valid; -// assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -// assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -// assign update_dtlb.vpn = update_ptw_dtlb.vpn; -// assign update_dtlb.asid = update_ptw_dtlb.asid; -// assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -// assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -// assign update_dtlb.content.d = update_ptw_dtlb.content.d; -// assign update_dtlb.content.a = update_ptw_dtlb.content.a; -// assign update_dtlb.content.g = update_ptw_dtlb.content.g; -// assign update_dtlb.content.u = update_ptw_dtlb.content.u; -// assign update_dtlb.content.x = update_ptw_dtlb.content.x; -// assign update_dtlb.content.w = update_ptw_dtlb.content.w; -// assign update_dtlb.content.r = update_ptw_dtlb.content.r; -// assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -// assign update_itlb.valid = update_ptw_itlb.valid; -// assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -// assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -// assign update_itlb.vpn = update_ptw_itlb.vpn; -// assign update_itlb.asid = update_ptw_itlb.asid; -// assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -// assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -// assign update_itlb.content.d = update_ptw_itlb.content.d; -// assign update_itlb.content.a = update_ptw_itlb.content.a; -// assign update_itlb.content.g = update_ptw_itlb.content.g; -// assign update_itlb.content.u = update_ptw_itlb.content.u; -// assign update_itlb.content.x = update_ptw_itlb.content.x; -// assign update_itlb.content.w = update_ptw_itlb.content.w; -// assign update_itlb.content.r = update_ptw_itlb.content.r; -// assign update_itlb.content.v = update_ptw_itlb.content.v; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -360,7 +298,6 @@ module cva6_mmu always_comb begin : instr_interface // MMU disabled: just pass through icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation // two potential exception 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 @@ -385,17 +322,6 @@ module cva6_mmu icache_areq_o.fetch_valid = 1'b0; - // // 4K page - // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_page[1]) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_page[0]) begin - // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; - // end - // --------- // ITLB Hit // -------- @@ -480,7 +406,6 @@ module cva6_mmu // Wires to PMP checks riscv::pmp_access_t pmp_access_type; logic pmp_data_allow; -// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = @@ -519,9 +444,6 @@ module cva6_mmu dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; dtlb_is_page_n = dtlb_is_page; - - // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; - // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; lsu_valid_o = lsu_req_q; lsu_exception_o = misaligned_ex_q; pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; @@ -536,19 +458,7 @@ module cva6_mmu // translation is enabled and no misaligned exception occurred if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; - // // 4K page - // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - // lsu_dtlb_ppn_o = dtlb_content.ppn; - // // Mega page - // if (dtlb_is_2M_q) begin - // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; - // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; - // end - // // Giga page - // if (dtlb_is_1G_q) begin - // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; - // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; - // end + // --------- // DTLB Hit // -------- From e9336d0af9b13aeea71f9f50ca7655b1cd141ec6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:33:00 +0100 Subject: [PATCH 37/64] Revert "Revert "top mmu with no shared tlb, common top with no common exceptions"" This reverts commit db49cf4f75b6ead5a63cf996220a5e9b3ac0d4ff. --- core/mmu_unify/cva6_mmu.sv | 1058 ++++++++++++++++++++---------------- 1 file changed, 604 insertions(+), 454 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 4b73d14079..590fcbe45d 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Thales. +// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -8,257 +8,307 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Sebastien Jacq Thales Research & Technology -// Date: 17/07/2021 -// -// Additional contributions by: -// Sebastien Jacq - sjthales on github.com -// -// Description: Memory Management Unit for CV32A6, contains TLB and -// address translation unit. Sv32 as defined in RISC-V -// privilege specification 1.11-WIP. -// This module is an adaptation of the MMU Sv39 developed -// by Florian Zaruba to the Sv32 standard. -// -// =========================================================================== // -// Revisions : -// Date Version Author Description -// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6 -// =========================================================================== // +// Author: Florian Zaruba, ETH Zurich +// Date: 19/04/2017 +// Description: Memory Management Unit for Ariane, contains TLB and +// address translation unit. SV39 as defined in RISC-V +// privilege specification 1.11-WIP + module cva6_mmu - import ariane_pkg::*; +import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 2, - parameter int unsigned DATA_TLB_ENTRIES = 2, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, - // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, - // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); - logic iaccess_err; // insufficient privilege to access this instruction page - logic daccess_err; // insufficient privilege to access this data page - logic ptw_active; // PTW is currently walking a page table - logic walking_instr; // PTW is walking because of an ITLB miss - logic ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - - logic [riscv::VLEN-1:0] update_vaddr; - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - tlb_update_sv32_t update_itlb_sv32, update_dtlb_sv32, update_shared_tlb_sv32; - - logic itlb_lu_access; - riscv::pte_cva6_t itlb_content; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - - logic dtlb_lu_access; - riscv::pte_cva6_t dtlb_content; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic dtlb_lu_hit; - - logic shared_tlb_access; - logic [riscv::VLEN-1:0] shared_tlb_vaddr; - logic shared_tlb_hit; - - logic itlb_req; - - - // Assignments - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - cva6_shared_tlb #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), - - // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) - ); - - cva6_ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), - // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now +logic iaccess_err; // insufficient privilege to access this instruction page +logic daccess_err; // insufficient privilege to access this data page +logic ptw_active; // PTW is currently walking a page table +logic walking_instr; // PTW is walking because of an ITLB miss +logic ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + +logic [riscv::VLEN-1:0] update_vaddr; +tlb_update_t update_ptw_itlb, update_ptw_dtlb; +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +riscv::pte_cva6_t itlb_content; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; + +logic dtlb_lu_access; +riscv::pte_cva6_t dtlb_content; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic dtlb_lu_hit; + +logic shared_tlb_access; +logic [riscv::VLEN-1:0] shared_tlb_vaddr; +logic shared_tlb_hit; + + +// Assignments +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); - // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); - ); +// cva6_shared_tlb #( +// .CVA6Cfg (CVA6Cfg), +// .SHARED_TLB_DEPTH(64), +// .SHARED_TLB_WAYS (2), +// .ASID_WIDTH (ASID_WIDTH), +// .ASID_LEN (ASID_LEN), +// .VPN_LEN(VPN_LEN), +// .PT_LEVELS(PT_LEVELS) +// ) i_shared_tlb ( +// .clk_i (clk_i), +// .rst_ni (rst_ni), +// .flush_i(flush_tlb_i), + +// .enable_translation_i (enable_translation_i), +// .en_ld_st_translation_i(en_ld_st_translation_i), + +// .asid_i (asid_i), +// // from TLBs +// // did we miss? +// .itlb_access_i(itlb_lu_access), +// .itlb_hit_i (itlb_lu_hit), +// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + +// .dtlb_access_i(dtlb_lu_access), +// .dtlb_hit_i (dtlb_lu_hit), +// .dtlb_vaddr_i (lsu_vaddr_i), + +// // to TLBs, update logic +// .itlb_update_o(update_itlb), +// .dtlb_update_o(update_dtlb), + +// // Performance counters +// .itlb_miss_o(itlb_miss_o), +// .dtlb_miss_o(dtlb_miss_o), + +// .shared_tlb_access_o(shared_tlb_access), +// .shared_tlb_hit_o (shared_tlb_hit), +// .shared_tlb_vaddr_o (shared_tlb_vaddr), + +// .itlb_req_o (itlb_req), +// // to update shared tlb +// .shared_tlb_update_i(update_shared_tlb) +// ); + +// cva6_ptw #( +// .CVA6Cfg (CVA6Cfg), +// .ASID_WIDTH(ASID_WIDTH), +// .VPN_LEN(VPN_LEN), +// .PT_LEVELS(PT_LEVELS) +// ) i_ptw ( +// .clk_i (clk_i), +// .rst_ni (rst_ni), +// .flush_i(flush_i), + +// .ptw_active_o (ptw_active), +// .walking_instr_o (walking_instr), +// .ptw_error_o (ptw_error), +// .ptw_access_exception_o(ptw_access_exception), + +// .lsu_is_store_i(lsu_is_store_i), +// // PTW memory interface +// .req_port_i (req_port_i), +// .req_port_o (req_port_o), + +// // to Shared TLB, update logic +// .shared_tlb_update_o(update_shared_tlb), + +// .update_vaddr_o(update_vaddr), + +// .asid_i(asid_i), + +// // from shared TLB +// // did we miss? +// .shared_tlb_access_i(shared_tlb_access), +// .shared_tlb_hit_i (shared_tlb_hit), +// .shared_tlb_vaddr_i (shared_tlb_vaddr), + +// .itlb_req_i(itlb_req), + +// // from CSR file +// .satp_ppn_i(satp_ppn_i), // ppn from satp +// .mxr_i (mxr_i), + +// // Performance counters +// .shared_tlb_miss_o(), //open for now + +// // PMP +// .pmpcfg_i (pmpcfg_i), +// .pmpaddr_i (pmpaddr_i), +// .bad_paddr_o(ptw_bad_paddr) + +// ); + +ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH) +) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + .enable_translation_i (enable_translation_i), + + .update_vaddr_o(update_vaddr), + .itlb_update_o (update_ptw_itlb), + .dtlb_update_o (update_ptw_dtlb), + + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + .req_port_i (req_port_i), + .req_port_o (req_port_o), + .pmpcfg_i, + .pmpaddr_i, + .bad_paddr_o(ptw_bad_paddr), + .* +); +assign update_dtlb.valid = update_ptw_dtlb.valid; +assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; +assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; +assign update_dtlb.vpn = update_ptw_dtlb.vpn; +assign update_dtlb.asid = update_ptw_dtlb.asid; +assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; +assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; +assign update_dtlb.content.d = update_ptw_dtlb.content.d; +assign update_dtlb.content.a = update_ptw_dtlb.content.a; +assign update_dtlb.content.g = update_ptw_dtlb.content.g; +assign update_dtlb.content.u = update_ptw_dtlb.content.u; +assign update_dtlb.content.x = update_ptw_dtlb.content.x; +assign update_dtlb.content.w = update_ptw_dtlb.content.w; +assign update_dtlb.content.r = update_ptw_dtlb.content.r; +assign update_dtlb.content.v = update_ptw_dtlb.content.v; + +assign update_itlb.valid = update_ptw_itlb.valid; +assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; +assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; +assign update_itlb.vpn = update_ptw_itlb.vpn; +assign update_itlb.asid = update_ptw_itlb.asid; +assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; +assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; +assign update_itlb.content.d = update_ptw_itlb.content.d; +assign update_itlb.content.a = update_ptw_itlb.content.a; +assign update_itlb.content.g = update_ptw_itlb.content.g; +assign update_itlb.content.u = update_ptw_itlb.content.u; +assign update_itlb.content.x = update_ptw_itlb.content.x; +assign update_itlb.content.w = update_ptw_itlb.content.w; +assign update_itlb.content.r = update_ptw_itlb.content.r; +assign update_itlb.content.v = update_ptw_itlb.content.v; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -269,8 +319,8 @@ module cva6_mmu // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4 // .probe5(ptw_error), // input wire [1:0] probe5 // .probe6(update_vaddr), // input wire [0:0] probe6 -// .probe7(update_itlb.valid), // input wire [0:0] probe7 -// .probe8(update_dtlb.valid), // input wire [0:0] probe8 +// .probe7(update_ptw_itlb.valid), // input wire [0:0] probe7 +// .probe8(update_ptw_dtlb.valid), // input wire [0:0] probe8 // .probe9(dtlb_lu_access), // input wire [0:0] probe9 // .probe10(lsu_vaddr_i), // input wire [0:0] probe10 // .probe11(dtlb_lu_hit), // input wire [0:0] probe11 @@ -282,85 +332,224 @@ module cva6_mmu //----------------------- // Instruction Interface //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + +assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; +assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar a; +generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))] = // - (enable_translation_i && (|itlb_is_page[PT_LEVELS-2:a]==0)) ? // - itlb_content.ppn [((VPN_LEN/PT_LEVELS)*(a+1))-1:((VPN_LEN/PT_LEVELS)*(a))] : // - icache_areq_i.fetch_vaddr[12+((VPN_LEN/PT_LEVELS)*(a+1))-1:12+((VPN_LEN/PT_LEVELS)*(a))]; - end +endgenerate + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - endgenerate - - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + 1'b1 + }; + end +end - icache_areq_o.fetch_valid = 1'b0; - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); + +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; + + +assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + + endgenerate // The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, 1'b1 }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 }; else icache_areq_o.fetch_exception = { @@ -444,155 +633,116 @@ module cva6_mmu lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; end - endgenerate - - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; //only 32 bits on 34b of lsu_paddr_o are returned. - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end end - end - - // Load/store PMP check - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 + }; end end +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end endmodule \ No newline at end of file From 950cbb4cf2511716936d10f6e1edbab59fc64338 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:33:55 +0100 Subject: [PATCH 38/64] Revert "Revert "parameterize top no exceptions based on sv39 with no shared tlb"" This reverts commit 23f5b10a3a1b8bf8be92bfc8d088cbdd5704940f. --- core/mmu_unify/cva6_mmu.sv | 138 +++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 53 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 590fcbe45d..6ef0ce41cd 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,35 +329,36 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- +//----------------------- +// Instruction Interface +//----------------------- logic match_any_execute_region; logic pmp_instr_allow; localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; -assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; -assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar a; -generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end + assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; + assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + genvar a; + generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end -endgenerate + endgenerate // The instruction interface is a simple request response interface always_comb begin : instr_interface // MMU disabled: just pass through icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation // two potential exception 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 @@ -383,6 +384,17 @@ always_comb begin : instr_interface icache_areq_o.fetch_valid = 1'b0; + // // 4K page + // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // // Mega page + // if (itlb_is_page[1]) begin + // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; + // end + // // Giga page + // if (itlb_is_page[0]) begin + // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; + // end + // --------- // ITLB Hit // -------- @@ -458,12 +470,13 @@ pmp #( // Data Interface //----------------------- logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_t dtlb_pte_n, dtlb_pte_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; +logic dtlb_is_2M_n, dtlb_is_2M_q; +logic dtlb_is_1G_n, dtlb_is_1G_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -471,35 +484,36 @@ assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; logic pmp_data_allow; +// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end -assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; -assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; - end - - endgenerate // The data interface is simpler and only consists of a request/response interface + endgenerate +// The data interface is simpler and only consists of a request/response interface always_comb begin : data_interface // save request and DTLB response lsu_vaddr_n = lsu_vaddr_i; @@ -508,7 +522,11 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + dtlb_is_2M_n = dtlb_is_page[1]; + dtlb_is_1G_n = dtlb_is_page[0]; + + // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; + // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; lsu_valid_o = lsu_req_q; lsu_exception_o = misaligned_ex_q; pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; @@ -523,7 +541,19 @@ always_comb begin : data_interface // translation is enabled and no misaligned exception occurred if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; - + // // 4K page + // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; + // lsu_dtlb_ppn_o = dtlb_content.ppn; + // // Mega page + // if (dtlb_is_2M_q) begin + // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; + // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; + // end + // // Giga page + // if (dtlb_is_1G_q) begin + // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; + // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; + // end // --------- // DTLB Hit // -------- @@ -734,7 +764,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + dtlb_is_2M_q <= '0; + dtlb_is_1G_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -742,7 +773,8 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + dtlb_is_2M_q <= dtlb_is_2M_n; + dtlb_is_1G_q <= dtlb_is_1G_n; end end -endmodule \ No newline at end of file +endmodule From 31fdec09acf569a6f74fa147782f7ba5de8c5571 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:34:13 +0100 Subject: [PATCH 39/64] Revert "Revert "change all mmu to "is_page" concept"" This reverts commit 669208d2e1e60d7d0cdbd2d86133f3b00f49c5c7. --- core/mmu_unify/cva6_mmu.sv | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6ef0ce41cd..51427ccd2b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -475,8 +475,7 @@ exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; logic dtlb_hit_n, dtlb_hit_q; -logic dtlb_is_2M_n, dtlb_is_2M_q; -logic dtlb_is_1G_n, dtlb_is_1G_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; @@ -522,8 +521,7 @@ always_comb begin : data_interface dtlb_pte_n = dtlb_content; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; - dtlb_is_2M_n = dtlb_is_page[1]; - dtlb_is_1G_n = dtlb_is_page[0]; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; @@ -764,8 +762,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_2M_q <= '0; - dtlb_is_1G_q <= '0; + dtlb_is_page_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_req_q <= lsu_req_n; @@ -773,8 +770,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin dtlb_pte_q <= dtlb_pte_n; dtlb_hit_q <= dtlb_hit_n; lsu_is_store_q <= lsu_is_store_n; - dtlb_is_2M_q <= dtlb_is_2M_n; - dtlb_is_1G_q <= dtlb_is_1G_n; + dtlb_is_page_q <= dtlb_is_page_n; end end endmodule From b4e86168146bc4b1ba20e99804ca4fe5f9b72f81 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:36:20 +0100 Subject: [PATCH 40/64] Revert "Revert "add common exceptions in top"" This reverts commit 2a9c97b86635cf1c58a0fa3cf806e4c1aa8b8194. --- core/mmu_unify/cva6_mmu.sv | 510 ++++++++++++++++++------------------- 1 file changed, 254 insertions(+), 256 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 51427ccd2b..9f1bad007b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -329,12 +329,12 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); -//----------------------- -// Instruction Interface -//----------------------- -logic match_any_execute_region; -logic pmp_instr_allow; -localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + //----------------------- + // Instruction Interface + //----------------------- + logic match_any_execute_region; + logic pmp_instr_allow; + localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // @@ -354,35 +354,35 @@ localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; endgenerate -// The instruction interface is a simple request response interface -always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + // The instruction interface is a simple request response interface + always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && enable_translation_i + && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - icache_areq_o.fetch_valid = 1'b0; + icache_areq_o.fetch_valid = 1'b0; // // 4K page // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; @@ -395,94 +395,90 @@ always_comb begin : instr_interface // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; // end - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; end end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, - 1'b1 - }; - end -end -// check for execute flag on memory -assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} -); + // check for execute flag on memory + assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} + ); -// Instruction fetch -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) -); + // Instruction fetch + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) + ); + + //----------------------- + // Data Interface + //----------------------- + logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; + exception_t misaligned_ex_n, misaligned_ex_q; + logic lsu_req_n, lsu_req_q; + logic lsu_is_store_n, lsu_is_store_q; + logic dtlb_hit_n, dtlb_hit_q; + logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; -//----------------------- -// Data Interface -//----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; -exception_t misaligned_ex_n, misaligned_ex_q; -logic lsu_req_n, lsu_req_q; -logic lsu_is_store_n, lsu_is_store_q; -logic dtlb_hit_n, dtlb_hit_q; -logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - -// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) -assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - -// Wires to PMP checks -riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; + // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) + assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + + // Wires to PMP checks + riscv::pmp_access_t pmp_access_type; + logic pmp_data_allow; // localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; @@ -512,33 +508,33 @@ logic pmp_data_allow; end endgenerate -// The data interface is simpler and only consists of a request/response interface -always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; + // The data interface is simpler and only consists of a request/response interface + always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; // // 4K page // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; // lsu_dtlb_ppn_o = dtlb_content.ppn; @@ -552,17 +548,85 @@ always_comb begin : data_interface // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end + + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 + }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end + end + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin if (lsu_is_store_q) begin // check if the page is write-able and we are not violating privileges // also check if the dirty flag is set @@ -662,115 +726,49 @@ always_comb begin : data_interface end // this is a load + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - end + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1 - }; - end - end - end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1 - }; end end -end -// Load/store PMP check -pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) -) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) -); + // Load/store PMP check + pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + ) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) + ); -// ---------- -// Registers -// ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; - end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + // ---------- + // Registers + // ---------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end end -end -endmodule +endmodule \ No newline at end of file From 499d271ca311b545de9054e62e35ac7eebb9790c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:40:31 +0100 Subject: [PATCH 41/64] common mmu unified sv39 and sv32 --- core/mmu_unify/cva6_mmu.sv | 994 +++++++++++++++---------------------- 1 file changed, 403 insertions(+), 591 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 9f1bad007b..db40c64cd6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -1,4 +1,4 @@ -// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright (c) 2021 Thales. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -8,11 +8,13 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Florian Zaruba, ETH Zurich -// Date: 19/04/2017 -// Description: Memory Management Unit for Ariane, contains TLB and -// address translation unit. SV39 as defined in RISC-V -// privilege specification 1.11-WIP +// Author: Angela Gonzalez, PlanV Technology +// Date: 07/12/2023 +// Description: Memory Management Unit for CVA6, contains TLB and +// address translation unit. SV32 and SV39 as defined in RISC-V +// privilege specification 1.11-WIP. +// This module is an merge of the MMU Sv39 developed +// by Florian Zaruba and the MMU Sv32 developed by Sebastien Jacq. module cva6_mmu @@ -80,7 +82,6 @@ logic ptw_access_exception; // PTW threw an access exception logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr logic [riscv::VLEN-1:0] update_vaddr; -tlb_update_t update_ptw_itlb, update_ptw_dtlb; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; logic itlb_lu_access; @@ -104,211 +105,149 @@ assign dtlb_lu_access = lsu_req_i; cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), - .update_i(update_itlb), + .update_i(update_itlb), - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) ); cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), - .update_i(update_dtlb), + .update_i(update_dtlb), - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) ); -// cva6_shared_tlb #( -// .CVA6Cfg (CVA6Cfg), -// .SHARED_TLB_DEPTH(64), -// .SHARED_TLB_WAYS (2), -// .ASID_WIDTH (ASID_WIDTH), -// .ASID_LEN (ASID_LEN), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_shared_tlb ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_tlb_i), - -// .enable_translation_i (enable_translation_i), -// .en_ld_st_translation_i(en_ld_st_translation_i), - -// .asid_i (asid_i), -// // from TLBs -// // did we miss? -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// // to TLBs, update logic -// .itlb_update_o(update_itlb), -// .dtlb_update_o(update_dtlb), - -// // Performance counters -// .itlb_miss_o(itlb_miss_o), -// .dtlb_miss_o(dtlb_miss_o), - -// .shared_tlb_access_o(shared_tlb_access), -// .shared_tlb_hit_o (shared_tlb_hit), -// .shared_tlb_vaddr_o (shared_tlb_vaddr), - -// .itlb_req_o (itlb_req), -// // to update shared tlb -// .shared_tlb_update_i(update_shared_tlb) -// ); - -// cva6_ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH), -// .VPN_LEN(VPN_LEN), -// .PT_LEVELS(PT_LEVELS) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .flush_i(flush_i), - -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), - -// .lsu_is_store_i(lsu_is_store_i), -// // PTW memory interface -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), - -// // to Shared TLB, update logic -// .shared_tlb_update_o(update_shared_tlb), - -// .update_vaddr_o(update_vaddr), - -// .asid_i(asid_i), - -// // from shared TLB -// // did we miss? -// .shared_tlb_access_i(shared_tlb_access), -// .shared_tlb_hit_i (shared_tlb_hit), -// .shared_tlb_vaddr_i (shared_tlb_vaddr), - -// .itlb_req_i(itlb_req), - -// // from CSR file -// .satp_ppn_i(satp_ppn_i), // ppn from satp -// .mxr_i (mxr_i), - -// // Performance counters -// .shared_tlb_miss_o(), //open for now - -// // PMP -// .pmpcfg_i (pmpcfg_i), -// .pmpaddr_i (pmpaddr_i), -// .bad_paddr_o(ptw_bad_paddr) - -// ); - -ptw #( +cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + + // Performance counters + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) +); + +cva6_ptw #( .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH) + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) ) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + .ptw_active_o (ptw_active), .walking_instr_o (walking_instr), .ptw_error_o (ptw_error), .ptw_access_exception_o(ptw_access_exception), - .enable_translation_i (enable_translation_i), + + .lsu_is_store_i(lsu_is_store_i), + // PTW memory interface + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), .update_vaddr_o(update_vaddr), - .itlb_update_o (update_ptw_itlb), - .dtlb_update_o (update_ptw_dtlb), - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + .asid_i(asid_i), - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) - .req_port_i (req_port_i), - .req_port_o (req_port_o), - .pmpcfg_i, - .pmpaddr_i, - .bad_paddr_o(ptw_bad_paddr), - .* ); -assign update_dtlb.valid = update_ptw_dtlb.valid; -assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -assign update_dtlb.vpn = update_ptw_dtlb.vpn; -assign update_dtlb.asid = update_ptw_dtlb.asid; -assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -assign update_dtlb.content.d = update_ptw_dtlb.content.d; -assign update_dtlb.content.a = update_ptw_dtlb.content.a; -assign update_dtlb.content.g = update_ptw_dtlb.content.g; -assign update_dtlb.content.u = update_ptw_dtlb.content.u; -assign update_dtlb.content.x = update_ptw_dtlb.content.x; -assign update_dtlb.content.w = update_ptw_dtlb.content.w; -assign update_dtlb.content.r = update_ptw_dtlb.content.r; -assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -assign update_itlb.valid = update_ptw_itlb.valid; -assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -assign update_itlb.vpn = update_ptw_itlb.vpn; -assign update_itlb.asid = update_ptw_itlb.asid; -assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -assign update_itlb.content.d = update_ptw_itlb.content.d; -assign update_itlb.content.a = update_ptw_itlb.content.a; -assign update_itlb.content.g = update_ptw_itlb.content.g; -assign update_itlb.content.u = update_ptw_itlb.content.u; -assign update_itlb.content.x = update_ptw_itlb.content.x; -assign update_itlb.content.w = update_ptw_itlb.content.w; -assign update_itlb.content.r = update_ptw_itlb.content.r; -assign update_itlb.content.v = update_ptw_itlb.content.v; + // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -329,304 +268,207 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + +assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; +assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar a; +generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end +endgenerate + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - endgenerate - - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && enable_translation_i - && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end - - icache_areq_o.fetch_valid = 1'b0; - - // // 4K page - // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_page[1]) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_page[0]) begin - // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; - // end - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end +end - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; -// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; - end - - endgenerate - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - - // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; - // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = en_ld_st_translation_i && ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u)); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - // // 4K page - // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - // lsu_dtlb_ppn_o = dtlb_content.ppn; - // // Mega page - // if (dtlb_is_2M_q) begin - // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; - // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; - // end - // // Giga page - // if (dtlb_is_1G_q) begin - // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; - // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; - // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); - // this is a store - if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end - - // this is a load - end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; + +assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + + endgenerate +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store if (lsu_is_store_q) begin // check if the page is write-able and we are not violating privileges // also check if the dirty flag is set @@ -639,136 +481,106 @@ assign update_itlb.content.v = update_ptw_itlb.content.v; // Check if any PMPs are violated end else if (!pmp_data_allow) begin lsu_exception_o = { - riscv::ST_ACCESS_FAULT, + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end + + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, 1'b1 }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; //to check on wave --> not connected - end - end - - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; - - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_pte_q.ppn [((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_q[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; - - assign lsu_dtlb_ppn_o[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[PT_LEVELS-2:i]==0)) ? // - dtlb_content.ppn[((VPN_LEN/PT_LEVELS)*(i+1))-1:((VPN_LEN/PT_LEVELS)*(i))] : // - lsu_vaddr_n[12+((VPN_LEN/PT_LEVELS)*(i+1))-1:12+((VPN_LEN/PT_LEVELS)*(i))]; end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (lsu_is_store_q) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end - // this is a load - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end - end - - // Load/store PMP check - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end endmodule \ No newline at end of file From 65668ab2746cea8be4c5f6ef38cb9775559c0284 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 14:50:39 +0100 Subject: [PATCH 42/64] correct page match assignment in shared tlb --- core/mmu_unify/cva6_shared_tlb.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index ba40875997..424cd0a62d 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -149,8 +149,9 @@ module cva6_shared_tlb generate for (i=0; i < SHARED_TLB_WAYS; i++) begin //identify page_match for all TLB Entries - assign page_match[i] = (shared_tag_rd[i].is_page[PT_LEVELS-2:0])*2 +1; + for (x=0; x < PT_LEVELS; x++) begin + assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; // assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; From 0bb4d310d137565ff1dba6e6386183274cabec91 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 7 Dec 2023 16:44:55 +0100 Subject: [PATCH 43/64] add missing itlb_req signal --- core/mmu_unify/cva6_mmu.sv | 1 + 1 file changed, 1 insertion(+) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index db40c64cd6..dab512a512 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -98,6 +98,7 @@ logic shared_tlb_access; logic [riscv::VLEN-1:0] shared_tlb_vaddr; logic shared_tlb_hit; +logic itlb_req; // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; From 48e0968b69600ad21ca9895261e8a7672d4e3136 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 09:38:24 +0100 Subject: [PATCH 44/64] common top clean up and fix lsu ppn o assignment in top --- core/mmu_unify/cva6_mmu.sv | 1124 +++++++++++++---------------- core/mmu_unify/cva6_ptw.sv | 5 + core/mmu_unify/cva6_shared_tlb.sv | 5 +- 3 files changed, 530 insertions(+), 604 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 50079f3173..51a6ea4309 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -20,297 +20,236 @@ module cva6_mmu import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic enable_translation_i, - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // LSU interface - // 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_t misaligned_ex_i, - input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1: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 - // Cycle 0 - output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB - output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) - // Cycle 1 - output logic lsu_valid_o, // translation is valid - output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address - output exception_t lsu_exception_o, // address translation threw an exception - // General control signals - input riscv::priv_lvl_t priv_lvl_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic sum_i, - input logic mxr_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, - input logic flush_tlb_i, + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic enable_translation_i, + input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // LSU interface + // 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_t misaligned_ex_i, + input logic lsu_req_i, // request address translation + input logic [riscv::VLEN-1: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 + // Cycle 0 + output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB + output logic [riscv::PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit) + // Cycle 1 + output logic lsu_valid_o, // translation is valid + output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address + output exception_t lsu_exception_o, // address translation threw an exception + // General control signals + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic sum_i, + input logic mxr_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic flush_tlb_i, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PTW memory interface + input dcache_req_o_t req_port_i, + output dcache_req_i_t req_port_o, + // PMP + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i +); + +logic iaccess_err; // insufficient privilege to access this instruction page +logic daccess_err; // insufficient privilege to access this data page +logic ptw_active; // PTW is currently walking a page table +logic walking_instr; // PTW is walking because of an ITLB miss +logic ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + +logic [riscv::VLEN-1:0] update_vaddr; +// tlb_update_t update_ptw_itlb, update_ptw_dtlb; +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +riscv::pte_cva6_t itlb_content; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; + +logic dtlb_lu_access; +riscv::pte_cva6_t dtlb_content; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic dtlb_lu_hit; + +logic shared_tlb_access; +logic [riscv::VLEN-1:0] shared_tlb_vaddr; +logic shared_tlb_hit; + +logic itlb_req; + +// Assignments +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + + .lu_is_page_o(itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .update_i(update_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (asid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + + .lu_is_page_o(dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + +cva6_shared_tlb #( + .CVA6Cfg (CVA6Cfg), + .SHARED_TLB_DEPTH(64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_shared_tlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_tlb_i), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .asid_i (asid_i), + // from TLBs + // did we miss? + .itlb_access_i(itlb_lu_access), + .itlb_hit_i (itlb_lu_hit), + .itlb_vaddr_i (icache_areq_i.fetch_vaddr), + + .dtlb_access_i(dtlb_lu_access), + .dtlb_hit_i (dtlb_lu_hit), + .dtlb_vaddr_i (lsu_vaddr_i), + + // to TLBs, update logic + .itlb_update_o(update_itlb), + .dtlb_update_o(update_dtlb), + // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .shared_tlb_access_o(shared_tlb_access), + .shared_tlb_hit_o (shared_tlb_hit), + .shared_tlb_vaddr_o (shared_tlb_vaddr), + + .itlb_req_o (itlb_req), + // to update shared tlb + .shared_tlb_update_i(update_shared_tlb) +); + +cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + .ASID_WIDTH(ASID_WIDTH), + .VPN_LEN(VPN_LEN), + .PT_LEVELS(PT_LEVELS) +) i_ptw ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i(flush_i), + + .ptw_active_o (ptw_active), + .walking_instr_o (walking_instr), + .ptw_error_o (ptw_error), + .ptw_access_exception_o(ptw_access_exception), + + .lsu_is_store_i(lsu_is_store_i), // PTW memory interface - input dcache_req_o_t req_port_i, - output dcache_req_i_t req_port_o, + .req_port_i (req_port_i), + .req_port_o (req_port_o), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + .update_vaddr_o(update_vaddr), + + .asid_i(asid_i), + + // from shared TLB + // did we miss? + .shared_tlb_access_i(shared_tlb_access), + .shared_tlb_hit_i (shared_tlb_hit), + .shared_tlb_vaddr_i (shared_tlb_vaddr), + + .itlb_req_i(itlb_req), + + // from CSR file + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), + + // Performance counters + .shared_tlb_miss_o(), //open for now + // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + ); - logic iaccess_err; // insufficient privilege to access this instruction page - logic daccess_err; // insufficient privilege to access this data page - logic ptw_active; // PTW is currently walking a page table - logic walking_instr; // PTW is walking because of an ITLB miss - logic ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr - - logic [riscv::VLEN-1:0] update_vaddr; - // tlb_update_t update_ptw_itlb, update_ptw_dtlb; - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - riscv::pte_cva6_t itlb_content; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - - logic dtlb_lu_access; - riscv::pte_cva6_t dtlb_content; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic dtlb_lu_hit; - - logic shared_tlb_access; - logic [riscv::VLEN-1:0] shared_tlb_vaddr; - logic shared_tlb_hit; - - - // Assignments - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .update_i(update_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (asid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - cva6_shared_tlb #( - .CVA6Cfg (CVA6Cfg), - .SHARED_TLB_DEPTH(64), - .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .asid_i (asid_i), - // from TLBs - // did we miss? - .itlb_access_i(itlb_lu_access), - .itlb_hit_i (itlb_lu_hit), - .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - - .dtlb_access_i(dtlb_lu_access), - .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), - - // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), - - // Performance counters - .itlb_miss_o(itlb_miss_o), - .dtlb_miss_o(dtlb_miss_o), - - .shared_tlb_access_o(shared_tlb_access), - .shared_tlb_hit_o (shared_tlb_hit), - .shared_tlb_vaddr_o (shared_tlb_vaddr), - - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) - ); - - cva6_ptw #( - .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) - ) i_ptw ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_i), - - .ptw_active_o (ptw_active), - .walking_instr_o (walking_instr), - .ptw_error_o (ptw_error), - .ptw_access_exception_o(ptw_access_exception), - - .lsu_is_store_i(lsu_is_store_i), - // PTW memory interface - .req_port_i (req_port_i), - .req_port_o (req_port_o), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i), - - // from shared TLB - // did we miss? - .shared_tlb_access_i(shared_tlb_access), - .shared_tlb_hit_i (shared_tlb_hit), - .shared_tlb_vaddr_i (shared_tlb_vaddr), - - .itlb_req_i(itlb_req), - - // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), - - // Performance counters - .shared_tlb_miss_o(), //open for now - - // PMP - .pmpcfg_i (pmpcfg_i), - .pmpaddr_i (pmpaddr_i), - .bad_paddr_o(ptw_bad_paddr) - - ); - -// ptw #( -// .CVA6Cfg (CVA6Cfg), -// .ASID_WIDTH(ASID_WIDTH) -// ) i_ptw ( -// .clk_i (clk_i), -// .rst_ni (rst_ni), -// .ptw_active_o (ptw_active), -// .walking_instr_o (walking_instr), -// .ptw_error_o (ptw_error), -// .ptw_access_exception_o(ptw_access_exception), -// .enable_translation_i (enable_translation_i), - -// .update_vaddr_o(update_vaddr), -// .itlb_update_o (update_ptw_itlb), -// .dtlb_update_o (update_ptw_dtlb), - -// .itlb_access_i(itlb_lu_access), -// .itlb_hit_i (itlb_lu_hit), -// .itlb_vaddr_i (icache_areq_i.fetch_vaddr), - -// .dtlb_access_i(dtlb_lu_access), -// .dtlb_hit_i (dtlb_lu_hit), -// .dtlb_vaddr_i (lsu_vaddr_i), - -// .req_port_i (req_port_i), -// .req_port_o (req_port_o), -// .pmpcfg_i, -// .pmpaddr_i, -// .bad_paddr_o(ptw_bad_paddr), -// .* -// ); -// assign update_dtlb.valid = update_ptw_dtlb.valid; -// assign update_dtlb.is_page[1] = update_ptw_dtlb.is_2M; -// assign update_dtlb.is_page[0] = update_ptw_dtlb.is_1G; -// assign update_dtlb.vpn = update_ptw_dtlb.vpn; -// assign update_dtlb.asid = update_ptw_dtlb.asid; -// assign update_dtlb.content.ppn = update_ptw_dtlb.content.ppn; -// assign update_dtlb.content.rsw = update_ptw_dtlb.content.rsw; -// assign update_dtlb.content.d = update_ptw_dtlb.content.d; -// assign update_dtlb.content.a = update_ptw_dtlb.content.a; -// assign update_dtlb.content.g = update_ptw_dtlb.content.g; -// assign update_dtlb.content.u = update_ptw_dtlb.content.u; -// assign update_dtlb.content.x = update_ptw_dtlb.content.x; -// assign update_dtlb.content.w = update_ptw_dtlb.content.w; -// assign update_dtlb.content.r = update_ptw_dtlb.content.r; -// assign update_dtlb.content.v = update_ptw_dtlb.content.v; - -// assign update_itlb.valid = update_ptw_itlb.valid; -// assign update_itlb.is_page[1] = update_ptw_itlb.is_2M; -// assign update_itlb.is_page[0] = update_ptw_itlb.is_1G; -// assign update_itlb.vpn = update_ptw_itlb.vpn; -// assign update_itlb.asid = update_ptw_itlb.asid; -// assign update_itlb.content.ppn = update_ptw_itlb.content.ppn; -// assign update_itlb.content.rsw = update_ptw_itlb.content.rsw; -// assign update_itlb.content.d = update_ptw_itlb.content.d; -// assign update_itlb.content.a = update_ptw_itlb.content.a; -// assign update_itlb.content.g = update_ptw_itlb.content.g; -// assign update_itlb.content.u = update_ptw_itlb.content.u; -// assign update_itlb.content.x = update_ptw_itlb.content.x; -// assign update_itlb.content.w = update_ptw_itlb.content.w; -// assign update_itlb.content.r = update_ptw_itlb.content.r; -// assign update_itlb.content.v = update_ptw_itlb.content.v; // ila_1 i_ila_1 ( // .clk(clk_i), // input wire clk @@ -331,347 +270,326 @@ module cva6_mmu // .probe14(itlb_lu_hit) // input wire [0:0] probe13 // ); - //----------------------- - // Instruction Interface - //----------------------- - logic match_any_execute_region; - logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; - assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // - (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); +//----------------------- +// Instruction Interface +//----------------------- +logic match_any_execute_region; +logic pmp_instr_allow; +localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + +assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; +assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // + (enable_translation_i) ? // + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +genvar a; +generate + + for (a=0; a < PT_LEVELS-1; a++) begin + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end - genvar a; - generate - - for (a=0; a < PT_LEVELS-1; a++) begin - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; - end +endgenerate + +// The instruction interface is a simple request response interface +always_comb begin : instr_interface + // MMU disabled: just pass through + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // two potential exception 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 + icache_areq_o.fetch_exception = '0; + // Check whether we are allowed to access this memory region from a fetch perspective + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + + // 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 + // an error. + if (enable_translation_i) begin + // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - endgenerate - - // The instruction interface is a simple request response interface - always_comb begin : instr_interface - // MMU disabled: just pass through - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0]; // play through in case we disabled address translation - // two potential exception 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 - icache_areq_o.fetch_exception = '0; - // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); - - // 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 - // an error. - if (enable_translation_i) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin + icache_areq_o.fetch_valid = 1'b0; + + // --------- + // ITLB Hit + // -------- + // if we hit the ITLB output the request signal immediately + if (itlb_lu_hit) begin + icache_areq_o.fetch_valid = icache_areq_i.fetch_req; + // we got an access error + if (iaccess_err) begin + // throw a page fault icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; + end else if (!pmp_instr_allow) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end + end else + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + if (ptw_active && walking_instr) begin + icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; + if (ptw_error) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end + // if it didn't match any execute region throw an `Instruction Access Fault` + // or: if we are not translating, check PMPs immediately on the paddr + if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; + end +end + +// check for execute flag on memory +assign match_any_execute_region = config_pkg::is_inside_execute_regions( + CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} +); - icache_areq_o.fetch_valid = 1'b0; - - // // 4K page - // icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_page[1]) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_page[0]) begin - // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; - // end - - // --------- - // ITLB Hit - // -------- - // if we hit the ITLB output the request signal immediately - if (itlb_lu_hit) begin - icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - // we got an access error - if (iaccess_err) begin - // throw a page fault - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_if ( + .addr_i (icache_areq_o.fetch_paddr), + .priv_lvl_i, + // we will always execute on the instruction fetch port + .access_type_i(riscv::ACCESS_EXEC), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_instr_allow) +); + +//----------------------- +// Data Interface +//----------------------- +logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +exception_t misaligned_ex_n, misaligned_ex_q; +logic lsu_req_n, lsu_req_q; +logic lsu_is_store_n, lsu_is_store_q; +logic dtlb_hit_n, dtlb_hit_q; +logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; + +// check if we need to do translation or if we are always ready (e.g.: we are not translating anything) +assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; + +// Wires to PMP checks +riscv::pmp_access_t pmp_access_type; +logic pmp_data_allow; + +assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + (riscv::PLEN-PPNWMin)'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +assign lsu_dtlb_ppn_o [11:0] = + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + dtlb_content.ppn[11:0] : // + lsu_vaddr_n[23:12]; + +genvar i; + generate + + for (i=0; i < PT_LEVELS-1; i++) begin + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // + lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + + assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + dtlb_content.ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] : // + (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN -1 : (24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-1) ) -1): (riscv::PLEN > riscv::VLEN) ? 24 :24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-2)])); + end + if(riscv::IS_XLEN64) begin + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; + end + + endgenerate +// The data interface is simpler and only consists of a request/response interface +always_comb begin : data_interface + // save request and DTLB response + lsu_vaddr_n = lsu_vaddr_i; + lsu_req_n = lsu_req_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + + // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions + misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; + + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it + // translation is enabled and no misaligned exception occurred + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + + // --------- + // DTLB Hit + // -------- + if (dtlb_hit_q && lsu_req_q) begin + lsu_valid_o = 1'b1; + // exception priority: + // PAGE_FAULTS have higher priority than ACCESS_FAULTS + // virtual memory based exceptions are PAGE_FAULTS + // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) + + // this is a store + if (lsu_is_store_q) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, 1'b1 }; - end else if (!pmp_instr_allow) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 + }; end - end else - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - if (ptw_active && walking_instr) begin - icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; - if (ptw_error) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + + // this is a load + end else begin + // check for sufficient access privileges - throw a page fault if necessary + if (daccess_err) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, + 1'b1 }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end - end - // if it didn't match any execute region throw an `Instruction Access Fault` - // or: if we are not translating, check PMPs immediately on the paddr - if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end - end - - // check for execute flag on memory - assign match_any_execute_region = config_pkg::is_inside_execute_regions( - CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr} - ); - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_if ( - .addr_i (icache_areq_o.fetch_paddr), - .priv_lvl_i, - // we will always execute on the instruction fetch port - .access_type_i(riscv::ACCESS_EXEC), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_instr_allow) - ); - - //----------------------- - // Data Interface - //----------------------- - logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; - exception_t misaligned_ex_n, misaligned_ex_q; - logic lsu_req_n, lsu_req_q; - logic lsu_is_store_n, lsu_is_store_q; - logic dtlb_hit_n, dtlb_hit_q; - logic [PT_LEVELS-2:0] dtlb_is_page_n, dtlb_is_page_q; - - // check if we need to do translation or if we are always ready (e.g.: we are not translating anything) - assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1; - - // Wires to PMP checks - riscv::pmp_access_t pmp_access_type; - logic pmp_data_allow; -// localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; - - assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - genvar i; - generate - - for (i=0; i < PT_LEVELS-1; i++) begin - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_pte_q.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_q[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - - assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(i+1)))] : // - lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; - end - if(riscv::IS_XLEN64) begin - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PPNW-1:PPNWMin+1] ; end - - endgenerate - // The data interface is simpler and only consists of a request/response interface - always_comb begin : data_interface - // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; - dtlb_hit_n = dtlb_lu_hit; - lsu_is_store_n = lsu_is_store_i; - dtlb_is_page_n = dtlb_is_page; - - // lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0]; - // lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - - // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions - misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i; - - // Check if the User flag is set, then we may only access it in supervisor mode - // if SUM is enabled - daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode - (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it - // translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - // // 4K page - // lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; - // lsu_dtlb_ppn_o = dtlb_content.ppn; - // // Mega page - // if (dtlb_is_2M_q) begin - // lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; - // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12]; - // end - // // Giga page - // if (dtlb_is_1G_q) begin - // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12]; - // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12]; - // end - // --------- - // DTLB Hit - // -------- - if (dtlb_hit_q && lsu_req_q) begin + end + end else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error) begin + // an error makes the translation valid lsu_valid_o = 1'b1; - // exception priority: - // PAGE_FAULTS have higher priority than ACCESS_FAULTS - // virtual memory based exceptions are PAGE_FAULTS - // physical memory based exceptions are ACCESS_FAULTS (PMA/PMP) - - // this is a store + // the page table walker can only throw page faults if (lsu_is_store_q) begin - // check if the page is write-able and we are not violating privileges - // also check if the dirty flag is set - if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, riscv::XLEN'(lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]), 1'b1 - }; - end - - // this is a load + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end else begin - // check for sufficient access privileges - throw a page fault if necessary - if (daccess_err) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q}, - 1'b1 - }; - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - end else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // the page table walker can only throw page faults - if (lsu_is_store_q) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - - if (ptw_access_exception) begin - // an error makes the translation valid - lsu_valid_o = 1'b1; - // Any fault of the page table walk should be based of the original access type - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end end - end // If translation is not enabled, check the paddr immediately against PMPs - else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin - if (lsu_is_store_q) begin - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + + if (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // Any fault of the page table walk should be based of the original access type + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end - end - - // Load/store PMP check - pmp #( - .CVA6Cfg (CVA6Cfg), - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - ) i_pmp_data ( - .addr_i (lsu_paddr_o), - .priv_lvl_i (ld_st_priv_lvl_i), - .access_type_i(pmp_access_type), - // Configuration - .conf_addr_i (pmpaddr_i), - .conf_i (pmpcfg_i), - .allow_o (pmp_data_allow) - ); - - // ---------- - // Registers - // ---------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - dtlb_hit_q <= '0; - lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + end // If translation is not enabled, check the paddr immediately against PMPs + else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin + if (lsu_is_store_q) begin + lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - dtlb_hit_q <= dtlb_hit_n; - lsu_is_store_q <= lsu_is_store_n; - dtlb_is_page_q <= dtlb_is_page_n; + lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end +end + +// Load/store PMP check +pmp #( + .CVA6Cfg (CVA6Cfg), + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) +) i_pmp_data ( + .addr_i (lsu_paddr_o), + .priv_lvl_i (ld_st_priv_lvl_i), + .access_type_i(pmp_access_type), + // Configuration + .conf_addr_i (pmpaddr_i), + .conf_i (pmpcfg_i), + .allow_o (pmp_data_allow) +); + +// ---------- +// Registers +// ---------- +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + dtlb_hit_q <= '0; + lsu_is_store_q <= '0; + dtlb_is_page_q <= '0; + end else begin + lsu_vaddr_q <= lsu_vaddr_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + dtlb_hit_q <= dtlb_hit_n; + lsu_is_store_q <= lsu_is_store_n; + dtlb_is_page_q <= dtlb_is_page_n; + end +end endmodule \ No newline at end of file diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index fb24401f42..783d9e5a6d 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -386,6 +386,11 @@ genvar x; else state_d = LATENCY; end end + + //for simulation purposes + initial begin + ptw_lvl_q <= 1; + end // sequential process always_ff @(posedge clk_i or negedge rst_ni) begin diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index e74421620d..bd2a278870 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -162,7 +162,10 @@ module cva6_shared_tlb genvar w; generate for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); + assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // + ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? // + dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); end endgenerate From 9c95d3d0b43279006958ab26a415a9e34c530dc1 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 09:38:50 +0100 Subject: [PATCH 45/64] change cv64a6 config pkg (TLB=2) --- core/include/cv64a6_imafdc_sv39_config_pkg.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/include/cv64a6_imafdc_sv39_config_pkg.sv b/core/include/cv64a6_imafdc_sv39_config_pkg.sv index 0865c0431d..7644769996 100644 --- a/core/include/cv64a6_imafdc_sv39_config_pkg.sv +++ b/core/include/cv64a6_imafdc_sv39_config_pkg.sv @@ -55,8 +55,8 @@ package cva6_config_pkg; localparam CVA6ConfigNrStorePipeRegs = 0; localparam CVA6ConfigNrLoadBufEntries = 2; - localparam CVA6ConfigInstrTlbEntries = 16; - localparam CVA6ConfigDataTlbEntries = 16; + localparam CVA6ConfigInstrTlbEntries = 2; + localparam CVA6ConfigDataTlbEntries = 2; localparam CVA6ConfigRASDepth = 2; localparam CVA6ConfigBTBEntries = 32; From 219ad69c18cafc19a5d42268533026fb25aef080 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 13:36:48 +0100 Subject: [PATCH 46/64] parametrization compliance update and cleanup --- core/mmu_unify/cva6_mmu.sv | 46 ++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c256ae2f1c..cf65db5fc3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -73,6 +73,30 @@ import ariane_pkg::*; input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); + // memory management, pte for cva6 +localparam type pte_cva6_t = struct packed { +// typedef struct packed { + logic [riscv::PPNW-1:0] ppn; // PPN length for + logic [1:0] rsw; + logic d; + logic a; + logic g; + logic u; + logic x; + logic w; + logic r; + logic v; +} ; + +localparam type tlb_update_cva6_t = struct packed { +// typedef struct packed { + logic valid; // valid flag + logic [PT_LEVELS-2:0] is_page; // + logic [VPN_LEN-1:0] vpn; // + logic [ASID_LEN-1:0] asid; // + pte_cva6_t content; +} ; + logic iaccess_err; // insufficient privilege to access this instruction page logic daccess_err; // insufficient privilege to access this data page logic ptw_active; // PTW is currently walking a page table @@ -86,12 +110,12 @@ logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; logic itlb_lu_access; -riscv::pte_cva6_t itlb_content; +pte_cva6_t itlb_content; logic [PT_LEVELS-2:0] itlb_is_page; logic itlb_lu_hit; logic dtlb_lu_access; -riscv::pte_cva6_t dtlb_content; +pte_cva6_t dtlb_content; logic [PT_LEVELS-2:0] dtlb_is_page; logic dtlb_lu_hit; @@ -112,7 +136,9 @@ cva6_tlb #( .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .PT_LEVELS(PT_LEVELS), + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t) ) i_itlb ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -137,7 +163,9 @@ cva6_tlb #( .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .PT_LEVELS(PT_LEVELS), + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t) ) i_dtlb ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -163,7 +191,9 @@ cva6_shared_tlb #( .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .PT_LEVELS(PT_LEVELS), + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t) ) i_shared_tlb ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -204,7 +234,9 @@ cva6_ptw #( .CVA6Cfg (CVA6Cfg), .ASID_WIDTH(ASID_WIDTH), .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS) + .PT_LEVELS(PT_LEVELS), + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t) ) i_ptw ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -394,7 +426,7 @@ pmp #( // Data Interface //----------------------- logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -riscv::pte_cva6_t dtlb_pte_n, dtlb_pte_q; +pte_cva6_t dtlb_pte_n, dtlb_pte_q; exception_t misaligned_ex_n, misaligned_ex_q; logic lsu_req_n, lsu_req_q; logic lsu_is_store_n, lsu_is_store_q; From ee545872f5fdb276e4b7b93260f00ffe1ce88ce8 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 14:06:48 +0100 Subject: [PATCH 47/64] parametrization compliance and cleanup in packages --- core/include/ariane_pkg.sv | 11 ----------- core/include/riscv_pkg.sv | 13 ------------- 2 files changed, 24 deletions(-) diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index cc5705cdc7..446f609a61 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -35,9 +35,6 @@ package ariane_pkg; ); // depending on the number of scoreboard entries we need that many bits // to uniquely identify the entry in the scoreboard localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1; - localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; - localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; - localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; localparam BITS_SATURATION_COUNTER = 2; localparam ISSUE_WIDTH = 1; @@ -770,14 +767,6 @@ package ariane_pkg; riscv::pte_sv32_t content; } tlb_update_sv32_t; - typedef struct packed { - logic valid; // valid flag - logic [PT_LEVELS-2:0] is_page; // - logic [VPN_LEN-1:0] vpn; // - logic [ASID_LEN-1:0] asid; // - riscv::pte_cva6_t content; -} tlb_update_cva6_t; - typedef enum logic [1:0] { FE_NONE, FE_INSTR_ACCESS_FAULT, diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 317bf2ac4d..6805c7f647 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -321,19 +321,6 @@ package riscv; logic v; } pte_sv32_t; - // memory management, pte for cva6 - typedef struct packed { - logic [PPNW-1:0] ppn; // PPN length for - logic [1:0] rsw; - logic d; - logic a; - logic g; - logic u; - logic x; - logic w; - logic r; - logic v; -} pte_cva6_t; // ---------------------- // Exception Cause Codes From f9a106bd74f81f023506b52052c39b17d65c5a99 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 14:08:42 +0100 Subject: [PATCH 48/64] parametrization compliance and cleanup in modules --- core/mmu_unify/cva6_ptw.sv | 29 ++++++++++++++++------------- core/mmu_unify/cva6_shared_tlb.sv | 12 +++++++----- core/mmu_unify/cva6_tlb.sv | 6 ++++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 783d9e5a6d..e7457c34a0 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -28,6 +28,8 @@ module cva6_ptw import ariane_pkg::*; #( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int ASID_WIDTH = 1, parameter int unsigned VPN_LEN = 1, @@ -80,8 +82,8 @@ module cva6_ptw logic data_rvalid_q; riscv::xlen_t data_rdata_q; - riscv::pte_cva6_t pte; - assign pte = riscv::pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); + pte_cva6_t pte; + assign pte = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); enum logic [2:0] { @@ -96,7 +98,8 @@ module cva6_ptw state_q, state_d; // page tables levels - logic [PT_LEVELS-1:0] ptw_lvl_q, ptw_lvl_n,misaligned_page; + logic [PT_LEVELS-1:0] misaligned_page; + logic [PT_LEVELS-2:0] ptw_lvl_n,ptw_lvl_q; // is this an instruction page table walk? logic is_instr_ptw_q, is_instr_ptw_n; @@ -138,12 +141,12 @@ genvar x; for (x=0; x < PT_LEVELS-1; x++) begin // update the correct page table level - assign shared_tlb_update_o.is_page[x] = (ptw_lvl_q == (x+1)); + assign shared_tlb_update_o.is_page[x] = (ptw_lvl_q == (x)); // check if the ppn is correctly aligned: // 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault // exception. - assign misaligned_page[x] = (ptw_lvl_q == (x+1)) && (pte.ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-x)-1:0] != '0); + assign misaligned_page[x] = (ptw_lvl_q == (x)) && (pte.ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-x)-1:0] != '0); //record the vaddr corresponding to each level assign vaddr_lvl[x] = vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-x-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-x-2))]; @@ -235,7 +238,7 @@ genvar x; IDLE: begin // by default we start with the top-most page table - ptw_lvl_n = 1; + ptw_lvl_n = 0; global_mapping_n = 1'b0; is_instr_ptw_n = 1'b0; // if we got a Shared TLB miss @@ -324,16 +327,16 @@ genvar x; end else begin // pointer to next level of page table - if (ptw_lvl_q == PT_LEVELS) begin + if (ptw_lvl_q == PT_LEVELS-1) begin // Should already be the last level page table => Error - ptw_lvl_n = PT_LEVELS; + ptw_lvl_n = PT_LEVELS-1; state_d = PROPAGATE_ERROR; end else begin // if (ptw_lvl_q == 1) begin // we are in the second level now ptw_lvl_n = ptw_lvl_q+1; - ptw_pptr_n = {pte.ppn, vaddr_lvl[ptw_lvl_q-1], (PT_LEVELS)'(0)}; + ptw_pptr_n = {pte.ppn, vaddr_lvl[ptw_lvl_q], (PT_LEVELS)'(0)}; state_d = WAIT_GRANT; // end end @@ -388,16 +391,16 @@ genvar x; end //for simulation purposes - initial begin - ptw_lvl_q <= 1; - end + // initial begin + // ptw_lvl_q <= 1; + // end // sequential process always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin state_q <= IDLE; is_instr_ptw_q <= 1'b0; - ptw_lvl_q <= 1; + ptw_lvl_q <= 0; tag_valid_q <= 1'b0; tlb_update_asid_q <= '0; vaddr_q <= '0; diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 894c15a88f..e26b7f63ff 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -20,6 +20,8 @@ module cva6_shared_tlb import ariane_pkg::*; #( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int SHARED_TLB_DEPTH = 64, parameter int SHARED_TLB_WAYS = 2, @@ -101,11 +103,11 @@ module cva6_shared_tlb logic [ SHARED_TLB_WAYS-1:0] pte_wr_en; logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr; - logic [$bits(riscv::pte_cva6_t)-1:0] pte_wr_data; + logic [$bits(pte_cva6_t)-1:0] pte_wr_data; logic [ SHARED_TLB_WAYS-1:0] pte_rd_en; logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr; - logic [$bits(riscv::pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; + logic [$bits(pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; logic [ SHARED_TLB_WAYS-1:0] pte_req; logic [ SHARED_TLB_WAYS-1:0] pte_we; @@ -116,7 +118,7 @@ module cva6_shared_tlb logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] page_match; logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] level_match; - riscv::pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; + pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; logic [riscv::VLEN-1-12:0] itlb_vpn_q; logic [riscv::VLEN-1-12:0] dtlb_vpn_q; @@ -375,7 +377,7 @@ module cva6_shared_tlb // PTE RAM sram #( - .DATA_WIDTH($bits(riscv::pte_cva6_t)), + .DATA_WIDTH($bits(pte_cva6_t)), .NUM_WORDS (SHARED_TLB_DEPTH) ) pte_sram ( .clk_i (clk_i), @@ -389,7 +391,7 @@ module cva6_shared_tlb .ruser_o(), .rdata_o(pte_rd_data[i]) ); - assign pte[i] = riscv::pte_cva6_t'(pte_rd_data[i]); + assign pte[i] = pte_cva6_t'(pte_rd_data[i]); end endmodule diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index cdab5f6275..60f8d2bc83 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -26,6 +26,8 @@ module cva6_tlb import ariane_pkg::*; #( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned TLB_ENTRIES = 4, parameter int unsigned ASID_WIDTH = 1, @@ -42,7 +44,7 @@ input logic lu_access_i, input logic [ASID_WIDTH-1:0] lu_asid_i, input logic [riscv::VLEN-1:0] lu_vaddr_i, - output riscv::pte_cva6_t lu_content_o, + output pte_cva6_t lu_content_o, input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, output logic [PT_LEVELS-2:0] lu_is_page_o, @@ -58,7 +60,7 @@ } [TLB_ENTRIES-1:0] tags_q, tags_n; - riscv::pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; + pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; From 91ae8b94b49e9c3a54663721e104a0b5d7e08487 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 13 Dec 2023 14:09:26 +0100 Subject: [PATCH 49/64] definition of MMU parameters in LSU --- core/load_store_unit.sv | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 76ad4fed03..14f523c0a3 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -170,14 +170,19 @@ module load_store_unit // .* // ); if (MMU_PRESENT) begin : gen_mmu + + localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; + localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; + localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + cva6_mmu #( .CVA6Cfg (CVA6Cfg), .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ariane_pkg::ASID_LEN), - .VPN_LEN (ariane_pkg::VPN_LEN), - .PT_LEVELS (ariane_pkg::PT_LEVELS) + .ASID_LEN (ASID_LEN), + .VPN_LEN (VPN_LEN), + .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( // misaligned bypass .misaligned_ex_i(misaligned_exception), From 257f6e081b1efb4fd1bd7f5fd7c9158c52c8cf2c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 14 Dec 2023 11:11:11 +0100 Subject: [PATCH 50/64] update pte_cva6_t and tlb_update_cva6_t data types to support hypervisor extension. Update all submodules to new data types as first step --- core/load_store_unit.sv | 24 +++++++++--- core/mmu_unify/cva6_mmu.sv | 64 +++++++++++++++++-------------- core/mmu_unify/cva6_ptw.sv | 4 +- core/mmu_unify/cva6_shared_tlb.sv | 14 +++---- core/mmu_unify/cva6_tlb.sv | 64 +++++++++++++++++-------------- 5 files changed, 98 insertions(+), 72 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 14f523c0a3..95e622f757 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -171,15 +171,26 @@ module load_store_unit // ); if (MMU_PRESENT) begin : gen_mmu + localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; - localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; + localparam VPN_LEN = (riscv::XLEN == 64) ? (HYP_EXT ? 29 : 27) : 20; localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + localparam int unsigned mmu_ASID_WIDTH [HYP_EXT:0] = {ASID_WIDTH}; + logic [mmu_ASID_WIDTH[0]-1:0] mmu_asid_i [HYP_EXT:0]; + logic [mmu_ASID_WIDTH[0]-1:0] mmu_asid_to_be_flushed_i [HYP_EXT:0]; + logic [riscv::VLEN-1:0] mmu_vaddr_to_be_flushed_i [HYP_EXT:0]; + logic [riscv::VLEN-1:0] mmu_lsu_vaddr_i[HYP_EXT:0]; + + assign mmu_asid_i[0] = asid_i; + assign mmu_asid_to_be_flushed_i[0] =asid_to_be_flushed_i; + assign mmu_vaddr_to_be_flushed_i[0] =vaddr_to_be_flushed_i; + assign mmu_lsu_vaddr_i[0]= mmu_vaddr; cva6_mmu #( .CVA6Cfg (CVA6Cfg), .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), + .ASID_WIDTH (mmu_ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) @@ -188,7 +199,7 @@ module load_store_unit .misaligned_ex_i(misaligned_exception), .lsu_is_store_i (st_translation_req), .lsu_req_i (translation_req), - .lsu_vaddr_i (mmu_vaddr), + .lsu_vaddr_i (mmu_lsu_vaddr_i), .lsu_valid_o (translation_valid), .lsu_paddr_o (mmu_paddr), .lsu_exception_o(mmu_exception), @@ -199,11 +210,14 @@ module load_store_unit .req_port_o (dcache_req_ports_o[0]), // icache address translation requests .icache_areq_i (icache_areq_i), - .asid_to_be_flushed_i, - .vaddr_to_be_flushed_i, + // .asid_to_be_flushed_i, + // .vaddr_to_be_flushed_i, .icache_areq_o (icache_areq_o), .pmpcfg_i, .pmpaddr_i, + .asid_i(mmu_asid_i), + .asid_to_be_flushed_i(mmu_asid_to_be_flushed_i), + .vaddr_to_be_flushed_i(mmu_vaddr_to_be_flushed_i), .* ); end else begin : gen_no_mmu diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index cf65db5fc3..bc3df48eb4 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -23,10 +23,11 @@ import ariane_pkg::*; parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned INSTR_TLB_ENTRIES = 4, parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_WIDTH [HYP_EXT:0]= {1}, parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter int unsigned PT_LEVELS = 1, + parameter logic HYP_EXT = 0 ) ( input logic clk_i, input logic rst_ni, @@ -41,7 +42,7 @@ import ariane_pkg::*; // in the LSU as we distinguish load and stores, what we do here is simple address translation input exception_t misaligned_ex_i, input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1:0] lsu_vaddr_i, // virtual address in + input logic [riscv::VLEN-1:0] lsu_vaddr_i[HYP_EXT:0], // 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 // Cycle 0 @@ -58,9 +59,9 @@ import ariane_pkg::*; input logic mxr_i, // input logic flag_mprv_i, input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH-1:0] asid_i, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT:0], + input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], input logic flush_tlb_i, // Performance counters output logic itlb_miss_o, @@ -91,12 +92,13 @@ localparam type pte_cva6_t = struct packed { localparam type tlb_update_cva6_t = struct packed { // typedef struct packed { logic valid; // valid flag - logic [PT_LEVELS-2:0] is_page; // + logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; // logic [VPN_LEN-1:0] vpn; // - logic [ASID_LEN-1:0] asid; // - pte_cva6_t content; + logic [HYP_EXT:0][ASID_LEN-1:0] asid; // + pte_cva6_t [HYP_EXT:0] content; } ; + logic iaccess_err; // insufficient privilege to access this instruction page logic daccess_err; // insufficient privilege to access this data page logic ptw_active; // PTW is currently walking a page table @@ -110,12 +112,12 @@ logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; logic itlb_lu_access; -pte_cva6_t itlb_content; +pte_cva6_t [HYP_EXT:0] itlb_content ; logic [PT_LEVELS-2:0] itlb_is_page; logic itlb_lu_hit; logic dtlb_lu_access; -pte_cva6_t dtlb_content; +pte_cva6_t [HYP_EXT:0] dtlb_content; logic [PT_LEVELS-2:0] dtlb_is_page; logic dtlb_lu_hit; @@ -129,6 +131,8 @@ logic itlb_req; assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; +logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0]; +assign lu_vaddr_i[0]=icache_areq_i.fetch_vaddr; cva6_tlb #( .CVA6Cfg (CVA6Cfg), @@ -150,11 +154,12 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_vaddr_i (lu_vaddr_i), .lu_content_o (itlb_content), .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit) + .lu_hit_o (itlb_lu_hit), + .v_st_enbl_i(1) ); cva6_tlb #( @@ -181,14 +186,15 @@ cva6_tlb #( .lu_content_o (dtlb_content), .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) + .lu_hit_o (dtlb_lu_hit), + .v_st_enbl_i(1) ); cva6_shared_tlb #( .CVA6Cfg (CVA6Cfg), .SHARED_TLB_DEPTH(64), .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH), + .ASID_WIDTH (ASID_WIDTH[0]), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), @@ -202,7 +208,7 @@ cva6_shared_tlb #( .enable_translation_i (enable_translation_i), .en_ld_st_translation_i(en_ld_st_translation_i), - .asid_i (asid_i), + .asid_i (asid_i[0]), // from TLBs // did we miss? .itlb_access_i(itlb_lu_access), @@ -211,7 +217,7 @@ cva6_shared_tlb #( .dtlb_access_i(dtlb_lu_access), .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i), + .dtlb_vaddr_i (lsu_vaddr_i[0]), // to TLBs, update logic .itlb_update_o(update_itlb), @@ -232,7 +238,7 @@ cva6_shared_tlb #( cva6_ptw #( .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH), + .ASID_WIDTH(ASID_WIDTH[0]), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), .pte_cva6_t(pte_cva6_t), @@ -257,7 +263,7 @@ cva6_ptw #( .update_vaddr_o(update_vaddr), - .asid_i(asid_i), + .asid_i(asid_i[0]), // from shared TLB // did we miss? @@ -312,8 +318,8 @@ localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // (enable_translation_i) ? // - itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + itlb_content[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + (riscv::PLEN-PPNWMin-1)'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); genvar a; generate @@ -321,7 +327,7 @@ generate for (a=0; a < PT_LEVELS-1; a++) begin assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + itlb_content[0].ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; end @@ -336,8 +342,8 @@ always_comb begin : instr_interface // 2. We got an access error because of insufficient permissions -> throw an access exception icache_areq_o.fetch_exception = '0; // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); // 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. @@ -444,11 +450,11 @@ assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - (riscv::PLEN-PPNWMin)'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); assign lsu_dtlb_ppn_o [11:0] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_content.ppn[11:0] : // + dtlb_content[0].ppn[11:0] : // lsu_vaddr_n[23:12]; genvar i; @@ -462,14 +468,14 @@ genvar i; assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content.ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] : // + dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] : // (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN -1 : (24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-1) ) -1): (riscv::PLEN > riscv::VLEN) ? 24 :24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-2)])); end if(riscv::IS_XLEN64) begin assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] : lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; end @@ -477,7 +483,7 @@ genvar i; // The data interface is simpler and only consists of a request/response interface always_comb begin : data_interface // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i; + lsu_vaddr_n = lsu_vaddr_i[0]; lsu_req_n = lsu_req_i; misaligned_ex_n = misaligned_ex_i; dtlb_pte_n = dtlb_content; diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index e7457c34a0..425e12e679 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -141,7 +141,7 @@ genvar x; for (x=0; x < PT_LEVELS-1; x++) begin // update the correct page table level - assign shared_tlb_update_o.is_page[x] = (ptw_lvl_q == (x)); + assign shared_tlb_update_o.is_page[0][x] = (ptw_lvl_q == (x)); // check if the ppn is correctly aligned: // 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault @@ -157,7 +157,7 @@ genvar x; // output the correct ASID - assign shared_tlb_update_o.asid = tlb_update_asid_q; + assign shared_tlb_update_o.asid[0] = tlb_update_asid_q; // set the global mapping bit assign shared_tlb_update_o.content = pte | (global_mapping_q << 5); diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index e26b7f63ff..49d1c26843 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -237,14 +237,14 @@ module cva6_shared_tlb if (itlb_req_q) begin itlb_update_o.valid = 1'b1; itlb_update_o.vpn = itlb_vpn_q; - itlb_update_o.is_page = shared_tag_rd[i].is_page; - itlb_update_o.asid = tlb_update_asid_q; + itlb_update_o.is_page[0] = shared_tag_rd[i].is_page; + itlb_update_o.asid[0] = tlb_update_asid_q; itlb_update_o.content = pte[i]; end else if (dtlb_req_q) begin dtlb_update_o.valid = 1'b1; dtlb_update_o.vpn = dtlb_vpn_q; - dtlb_update_o.is_page = shared_tag_rd[i].is_page; - dtlb_update_o.asid = tlb_update_asid_q; + dtlb_update_o.is_page[0] = shared_tag_rd[i].is_page; + dtlb_update_o.asid[0] = tlb_update_asid_q; dtlb_update_o.content = pte[i]; end end @@ -300,10 +300,10 @@ module cva6_shared_tlb end end //update_flush - assign shared_tag_wr.asid = shared_tlb_update_i.asid; + assign shared_tag_wr.asid = shared_tlb_update_i.asid[0]; // assign shared_tag_wr.vpn[1] = shared_tlb_update_i.vpn[19:10]; // assign shared_tag_wr.vpn[0] = shared_tlb_update_i.vpn[9:0]; - assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page[0]; genvar z; @@ -318,7 +318,7 @@ module cva6_shared_tlb assign tag_wr_data = shared_tag_wr; assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; - assign pte_wr_data = shared_tlb_update_i.content; + assign pte_wr_data = shared_tlb_update_i.content[0]; assign way_valid = shared_tag_valid_q[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]]; assign repl_way = (all_ways_valid) ? rnd_way : inv_way; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 60f8d2bc83..e28fb552fa 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -12,15 +12,15 @@ // Date: 20/11/2023 // // Description: Translation Lookaside Buffer, parameterizable to Sv32 or Sv39 , -// fully set-associative +// or sv39x4 fully set-associative // This module is an merge of the Sv32 TLB developed by Sebastien -// Jacq (Thales Research & Technology) and the Sv39 TLB developed -// by Florian Zaruba and David Schaffenrath. +// Jacq (Thales Research & Technology), the Sv39 TLB developed +// by Florian Zaruba and David Schaffenrath and the Sv39x4 by Bruno Sá. // // =========================================================================== // // Revisions : // Date Version Author Description -// 2023-11-20 0.1 A.Gonzalez Generic TLB for CVA6 +// 2023-12-13 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support // =========================================================================== // module cva6_tlb @@ -29,39 +29,43 @@ parameter type pte_cva6_t = logic, parameter type tlb_update_cva6_t = logic, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter logic HYP_EXT = 0, parameter int unsigned TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, + parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] + parameter int unsigned ASID_LEN[HYP_EXT:0] = 1, //[vmid_len,asid_len] parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low - input logic flush_i, // Flush signal + input logic [HYP_EXT*2:0] flush_i, // Flush signal [g_stage,vs stage, normal translation signal] + input logic [HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled // Update TLB input tlb_update_cva6_t update_i, // Lookup signals input logic lu_access_i, - input logic [ASID_WIDTH-1:0] lu_asid_i, - input logic [riscv::VLEN-1:0] lu_vaddr_i, - output pte_cva6_t lu_content_o, - input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, + input logic [ASID_WIDTH[0]-1:0] lu_asid_i [HYP_EXT:0], //[lu_vmid,lu_asid] + input logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0], //[gp_addr,vaddr] + output pte_cva6_t [HYP_EXT:0] lu_content_o , + input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], //[vmid,asid] + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], // [gpaddr,vaddr] output logic [PT_LEVELS-2:0] lu_is_page_o, output logic lu_hit_o ); // Sv32 defines two levels of page tables, Sv39 defines 3 struct packed { - logic [ASID_LEN-1:0] asid; - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [PT_LEVELS-2:0] is_page; + logic [HYP_EXT:0][ASID_LEN-1:0] asid; + logic [PT_LEVELS+HYP_EXT-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; + logic [HYP_EXT*2:0] v_st_enbl_i; // v_i,g-stage enabled, s-stage enabled logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; - pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; + pte_cva6_t [HYP_EXT:0][TLB_ENTRIES-1:0] content_q , content_n; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; + logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_vpn_match; @@ -78,21 +82,23 @@ //AND the page_match is also set //At level 0 the page match is always set, so this level will have a match //if all vpn levels match - genvar i,x; + genvar i,x,z; generate for (i=0; i < TLB_ENTRIES; i++) begin - //identify page_match for all TLB Entries - // assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; + + for(z=0;z= 1) + assert (ASID_WIDTH[0] >= 1) else begin $error("ASID width must be at least 1"); $stop(); From 478ff59a5ff314741a57305aa8ab6c52219629d9 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 14 Dec 2023 13:48:56 +0100 Subject: [PATCH 51/64] translation parameterized and content in tlb_update_t data type dimensions corrected --- core/mmu_unify/cva6_shared_tlb.sv | 4 ++-- core/mmu_unify/cva6_tlb.sv | 34 +++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 49d1c26843..06063a8133 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -239,13 +239,13 @@ module cva6_shared_tlb itlb_update_o.vpn = itlb_vpn_q; itlb_update_o.is_page[0] = shared_tag_rd[i].is_page; itlb_update_o.asid[0] = tlb_update_asid_q; - itlb_update_o.content = pte[i]; + itlb_update_o.content[0] = pte[i]; end else if (dtlb_req_q) begin dtlb_update_o.valid = 1'b1; dtlb_update_o.vpn = dtlb_vpn_q; dtlb_update_o.is_page[0] = shared_tag_rd[i].is_page; dtlb_update_o.asid[0] = tlb_update_asid_q; - dtlb_update_o.content = pte[i]; + dtlb_update_o.content[0] = pte[i]; end end end diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e28fb552fa..e33d7c27ea 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -57,13 +57,13 @@ struct packed { logic [HYP_EXT:0][ASID_LEN-1:0] asid; logic [PT_LEVELS+HYP_EXT-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; - logic [HYP_EXT*2:0] v_st_enbl_i; // v_i,g-stage enabled, s-stage enabled + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; - pte_cva6_t [HYP_EXT:0][TLB_ENTRIES-1:0] content_q , content_n; + pte_cva6_t [TLB_ENTRIES-1:0][HYP_EXT:0] content_q , content_n; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; @@ -72,6 +72,7 @@ logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_level_match; logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy + logic [TLB_ENTRIES-1:0] match_stage; //------------- // Translation //------------- @@ -86,15 +87,19 @@ generate for (i=0; i < TLB_ENTRIES; i++) begin + assign match_stage[i] = tags_q[i].v_st_enbl == v_st_enbl_i; + for(z=0;z>1; //the page size is indicated here + lu_content_o = content_q[i]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; end @@ -147,6 +157,7 @@ tags_n[i].asid = tags_q[i].asid; tags_n[i].is_page = tags_q[i].is_page; tags_n[i].valid = tags_q[i].valid; + tags_n[i].v_st_enbl =tags_q[i].v_st_enbl; if (flush_i) begin // invalidate logic // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) @@ -155,10 +166,10 @@ else if (asid_to_be_flushed_is0 && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) - else if ((!content_q[0][i].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + else if ((!content_q[i][0].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) - else if ((!content_q[0][i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!asid_to_be_flushed_is0)) + else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!asid_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // normal replacement end else if (update_i.valid & replace_en[i]) begin @@ -166,9 +177,10 @@ tags_n[i].asid = update_i.asid; tags_n[i].is_page= update_i.is_page; tags_n[i].valid = 1'b1; + tags_n[i].v_st_enbl = v_st_enbl_i; // and content as well - content_n[0][i] = update_i.content; + content_n[i] = update_i.content; end end end From 3e7f23febfbc0453588a9d98ae803a02f2060d30 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 11:41:56 +0100 Subject: [PATCH 52/64] complete tlb merge for sv39x4 v0 --- core/include/riscv_pkg.sv | 4 +++- core/mmu_unify/cva6_mmu.sv | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 6805c7f647..8b6ace69ae 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -39,7 +39,9 @@ package riscv; // Warning: VLEN must be superior or equal to PLEN localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length - + localparam GPLEN = (XLEN == 32) ? 34 : 41; + localparam GPPNW = (XLEN == 32) ? 22 : 29; + localparam GPPN2 = (XLEN == 32) ? riscv::VLEN-33 : 10; localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; localparam ModeW = (XLEN == 32) ? 1 : 4; diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index bc3df48eb4..599af928dd 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -92,7 +92,7 @@ localparam type pte_cva6_t = struct packed { localparam type tlb_update_cva6_t = struct packed { // typedef struct packed { logic valid; // valid flag - logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; // + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; // logic [VPN_LEN-1:0] vpn; // logic [HYP_EXT:0][ASID_LEN-1:0] asid; // pte_cva6_t [HYP_EXT:0] content; @@ -131,11 +131,10 @@ logic itlb_req; assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; -logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0]; -assign lu_vaddr_i[0]=icache_areq_i.fetch_vaddr; cva6_tlb #( .CVA6Cfg (CVA6Cfg), + .HYP_EXT(HYP_EXT), .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -154,7 +153,8 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lu_vaddr_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_gpaddr_o(), .lu_content_o (itlb_content), .lu_is_page_o(itlb_is_page), @@ -164,6 +164,7 @@ cva6_tlb #( cva6_tlb #( .CVA6Cfg (CVA6Cfg), + .HYP_EXT(HYP_EXT), .TLB_ENTRIES(DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -182,7 +183,8 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), + .lu_vaddr_i (lsu_vaddr_i[0]), + .lu_gpaddr_o(), .lu_content_o (dtlb_content), .lu_is_page_o(dtlb_is_page), From af0e57f3b05bd5e5ea8c54e532e030bbab9ee2ad Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 16:47:09 +0100 Subject: [PATCH 53/64] common tlb with hypervisor support --- core/mmu_unify/cva6_tlb.sv | 122 +++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 13 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e33d7c27ea..172dabc26a 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -28,13 +28,15 @@ #( parameter type pte_cva6_t = logic, parameter type tlb_update_cva6_t = logic, + // parameter ariane_pkg::ariane_cfg_t CVA6Cfg = ariane_pkg::ArianeDefaultConfig, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter logic HYP_EXT = 0, parameter int unsigned TLB_ENTRIES = 4, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - parameter int unsigned ASID_LEN[HYP_EXT:0] = 1, //[vmid_len,asid_len] + parameter int unsigned ASID_LEN = 1, //[vmid_len,asid_len] parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 + ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low @@ -45,7 +47,9 @@ // Lookup signals input logic lu_access_i, input logic [ASID_WIDTH[0]-1:0] lu_asid_i [HYP_EXT:0], //[lu_vmid,lu_asid] - input logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0], //[gp_addr,vaddr] + input logic [riscv::VLEN-1:0] lu_vaddr_i , //[gp_addr,vaddr] + output logic [riscv::GPLEN-1:0] lu_gpaddr_o, + // output logic [riscv::GPLEN-1:0] lu_gpaddr_o, output pte_cva6_t [HYP_EXT:0] lu_content_o , input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], //[vmid,asid] input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], // [gpaddr,vaddr] @@ -64,6 +68,7 @@ tags_q, tags_n; pte_cva6_t [TLB_ENTRIES-1:0][HYP_EXT:0] content_q , content_n; + pte_cva6_t g_content; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; @@ -94,12 +99,17 @@ end for (x=0; x < PT_LEVELS; x++) begin - //identify page_match for all TLB Entries - assign page_match[i][x] = x==0 ? 1 : &(tags_q[i].is_page[PT_LEVELS-1-x] | !v_st_enbl_i[PT_LEVELS-1-x]); + //identify page_match for all TLB Entries + assign page_match[i][x] = x==0 ? 1 :(HYP_EXT && x==(PT_LEVELS-1)? // + ((&v_st_enbl_i[HYP_EXT:0]) ? + ((tags_q[i].is_page[PT_LEVELS-x][0] && (tags_q[i].is_page[PT_LEVELS-1-x][1] || tags_q[i].is_page[PT_LEVELS-x][1])) // + || (tags_q[i].is_page[PT_LEVELS-x][1] && (tags_q[i].is_page[PT_LEVELS-1-x][0] || tags_q[i].is_page[PT_LEVELS-x][0]))): + tags_q[i].is_page[PT_LEVELS-x][0] && v_st_enbl_i[0] || tags_q[i].is_page[PT_LEVELS-1-x][1] && v_st_enbl_i[1]): + &(tags_q[i].is_page[PT_LEVELS-1-x] | !v_st_enbl_i[HYP_EXT:0])); //identify if vpn matches at all PT levels for all TLB entries assign vpn_match[i][x] = (HYP_EXT && x==(PT_LEVELS-1) && !v_st_enbl_i[0]) ? // - lu_vaddr_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x] && lu_vaddr_i[0][VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] == tags_q[i].vpn[x+1][VPN_LEN%PT_LEVELS-1:0]: // - lu_vaddr_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x] && lu_vaddr_i[VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] == tags_q[i].vpn[x+1][VPN_LEN%PT_LEVELS-1:0]: // + lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; //identify if there is a hit at each PT level for all TLB entries assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; //identify if virtual address vpn matches at all PT levels for all TLB entries @@ -107,12 +117,27 @@ //identify if there is a hit at each PT level for all TLB entries assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced - assign tags_n[i].vpn[x] = (!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; + assign tags_n[i].vpn[x] = (!flush_i[0] && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; end if(HYP_EXT) begin assign tags_n[i].vpn[PT_LEVELS+HYP_EXT-1][VPN_LEN%PT_LEVELS-1:0] =(!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] : tags_q[i].vpn[PT_LEVELS+HYP_EXT-1][VPN_LEN%PT_LEVELS-1:0]; - end + assign lu_gpaddr_o[29:12] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? + (v_st_enbl_i[0] ? + (page_match[i][1] ? (lu_vaddr_i[29:12]): + (page_match[i][2] ? {content_q[i][0].ppn[17:9],lu_vaddr_i[20:12]}: content_q[i][0].ppn[17:0] )) : + (lu_vaddr_i[29:12])) : + 0; + + + assign lu_gpaddr_o[(riscv::GPLEN-1):30] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? + (v_st_enbl_i[0] ? content_q[i][0].ppn[(riscv::GPPNW-1):18] : lu_vaddr_i[(riscv::GPLEN-1):30] ) : + 0; + assign lu_gpaddr_o[11:0] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? + lu_vaddr_i[11:0] : + 0; + + end end endgenerate @@ -124,6 +149,7 @@ lu_hit_o = 1'b0; lu_content_o = '{default: 0}; lu_is_page_o = 0; + g_content = '{default: 0}; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // first level match, this may be a page, check the ASID flags as well @@ -135,16 +161,33 @@ lu_content_o = content_q[i]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; + // Compute G-Stage PPN based on the gpaddr + if(HYP_EXT) begin + g_content = content_q[i][1]; + if(tags_q[i].is_page[1][1]) + g_content.ppn[8:0] = lu_gpaddr_o[20:12]; + if(tags_q[i].is_page[0][1]) + g_content.ppn[17:0] = lu_gpaddr_o[29:12]; + end end end end end - logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high - logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high + logic [HYP_EXT:0] asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high + logic [HYP_EXT:0] vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high + logic [TLB_ENTRIES-1:0] gpaddr_gppn0_match; + logic [TLB_ENTRIES-1:0] gpaddr_gppn1_match; + logic [TLB_ENTRIES-1:0] gpaddr_gppn2_match; + logic [TLB_ENTRIES-1:0] [(riscv::GPPNW-1):0] gppn; - assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i[0]); - assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i[0]); + genvar a; + generate + for(a=0;a Date: Fri, 15 Dec 2023 17:04:29 +0100 Subject: [PATCH 54/64] Revert "common tlb with hypervisor support" This reverts commit af0e57f3b05bd5e5ea8c54e532e030bbab9ee2ad. --- core/mmu_unify/cva6_tlb.sv | 122 ++++--------------------------------- 1 file changed, 13 insertions(+), 109 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 172dabc26a..e33d7c27ea 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -28,15 +28,13 @@ #( parameter type pte_cva6_t = logic, parameter type tlb_update_cva6_t = logic, - // parameter ariane_pkg::ariane_cfg_t CVA6Cfg = ariane_pkg::ArianeDefaultConfig, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter logic HYP_EXT = 0, parameter int unsigned TLB_ENTRIES = 4, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - parameter int unsigned ASID_LEN = 1, //[vmid_len,asid_len] + parameter int unsigned ASID_LEN[HYP_EXT:0] = 1, //[vmid_len,asid_len] parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 - ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low @@ -47,9 +45,7 @@ // Lookup signals input logic lu_access_i, input logic [ASID_WIDTH[0]-1:0] lu_asid_i [HYP_EXT:0], //[lu_vmid,lu_asid] - input logic [riscv::VLEN-1:0] lu_vaddr_i , //[gp_addr,vaddr] - output logic [riscv::GPLEN-1:0] lu_gpaddr_o, - // output logic [riscv::GPLEN-1:0] lu_gpaddr_o, + input logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0], //[gp_addr,vaddr] output pte_cva6_t [HYP_EXT:0] lu_content_o , input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], //[vmid,asid] input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], // [gpaddr,vaddr] @@ -68,7 +64,6 @@ tags_q, tags_n; pte_cva6_t [TLB_ENTRIES-1:0][HYP_EXT:0] content_q , content_n; - pte_cva6_t g_content; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; @@ -99,17 +94,12 @@ end for (x=0; x < PT_LEVELS; x++) begin - //identify page_match for all TLB Entries - assign page_match[i][x] = x==0 ? 1 :(HYP_EXT && x==(PT_LEVELS-1)? // - ((&v_st_enbl_i[HYP_EXT:0]) ? - ((tags_q[i].is_page[PT_LEVELS-x][0] && (tags_q[i].is_page[PT_LEVELS-1-x][1] || tags_q[i].is_page[PT_LEVELS-x][1])) // - || (tags_q[i].is_page[PT_LEVELS-x][1] && (tags_q[i].is_page[PT_LEVELS-1-x][0] || tags_q[i].is_page[PT_LEVELS-x][0]))): - tags_q[i].is_page[PT_LEVELS-x][0] && v_st_enbl_i[0] || tags_q[i].is_page[PT_LEVELS-1-x][1] && v_st_enbl_i[1]): - &(tags_q[i].is_page[PT_LEVELS-1-x] | !v_st_enbl_i[HYP_EXT:0])); + //identify page_match for all TLB Entries + assign page_match[i][x] = x==0 ? 1 : &(tags_q[i].is_page[PT_LEVELS-1-x] | !v_st_enbl_i[PT_LEVELS-1-x]); //identify if vpn matches at all PT levels for all TLB entries assign vpn_match[i][x] = (HYP_EXT && x==(PT_LEVELS-1) && !v_st_enbl_i[0]) ? // - lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x] && lu_vaddr_i[VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] == tags_q[i].vpn[x+1][VPN_LEN%PT_LEVELS-1:0]: // - lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + lu_vaddr_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x] && lu_vaddr_i[0][VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] == tags_q[i].vpn[x+1][VPN_LEN%PT_LEVELS-1:0]: // + lu_vaddr_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; //identify if there is a hit at each PT level for all TLB entries assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; //identify if virtual address vpn matches at all PT levels for all TLB entries @@ -117,27 +107,12 @@ //identify if there is a hit at each PT level for all TLB entries assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced - assign tags_n[i].vpn[x] = (!flush_i[0] && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; + assign tags_n[i].vpn[x] = (!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; end if(HYP_EXT) begin assign tags_n[i].vpn[PT_LEVELS+HYP_EXT-1][VPN_LEN%PT_LEVELS-1:0] =(!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] : tags_q[i].vpn[PT_LEVELS+HYP_EXT-1][VPN_LEN%PT_LEVELS-1:0]; - assign lu_gpaddr_o[29:12] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? - (v_st_enbl_i[0] ? - (page_match[i][1] ? (lu_vaddr_i[29:12]): - (page_match[i][2] ? {content_q[i][0].ppn[17:9],lu_vaddr_i[20:12]}: content_q[i][0].ppn[17:0] )) : - (lu_vaddr_i[29:12])) : - 0; - - - assign lu_gpaddr_o[(riscv::GPLEN-1):30] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? - (v_st_enbl_i[0] ? content_q[i][0].ppn[(riscv::GPPNW-1):18] : lu_vaddr_i[(riscv::GPLEN-1):30] ) : - 0; - assign lu_gpaddr_o[11:0] = (tags_q[i].valid && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) ? - lu_vaddr_i[11:0] : - 0; - - end + end end endgenerate @@ -149,7 +124,6 @@ lu_hit_o = 1'b0; lu_content_o = '{default: 0}; lu_is_page_o = 0; - g_content = '{default: 0}; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // first level match, this may be a page, check the ASID flags as well @@ -161,33 +135,16 @@ lu_content_o = content_q[i]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; - // Compute G-Stage PPN based on the gpaddr - if(HYP_EXT) begin - g_content = content_q[i][1]; - if(tags_q[i].is_page[1][1]) - g_content.ppn[8:0] = lu_gpaddr_o[20:12]; - if(tags_q[i].is_page[0][1]) - g_content.ppn[17:0] = lu_gpaddr_o[29:12]; - end end end end end - logic [HYP_EXT:0] asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high - logic [HYP_EXT:0] vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high - logic [TLB_ENTRIES-1:0] gpaddr_gppn0_match; - logic [TLB_ENTRIES-1:0] gpaddr_gppn1_match; - logic [TLB_ENTRIES-1:0] gpaddr_gppn2_match; - logic [TLB_ENTRIES-1:0] [(riscv::GPPNW-1):0] gppn; + logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high + logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high - genvar a; - generate - for(a=0;a Date: Fri, 15 Dec 2023 17:08:21 +0100 Subject: [PATCH 55/64] Revert "complete tlb merge for sv39x4 v0" This reverts commit 3e7f23febfbc0453588a9d98ae803a02f2060d30. --- core/include/riscv_pkg.sv | 4 +--- core/mmu_unify/cva6_mmu.sv | 12 +++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 8b6ace69ae..6805c7f647 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -39,9 +39,7 @@ package riscv; // Warning: VLEN must be superior or equal to PLEN localparam VLEN = (XLEN == 32) ? 32 : 64; // virtual address length localparam PLEN = (XLEN == 32) ? 34 : 56; // physical address length - localparam GPLEN = (XLEN == 32) ? 34 : 41; - localparam GPPNW = (XLEN == 32) ? 22 : 29; - localparam GPPN2 = (XLEN == 32) ? riscv::VLEN-33 : 10; + localparam IS_XLEN32 = (XLEN == 32) ? 1'b1 : 1'b0; localparam IS_XLEN64 = (XLEN == 32) ? 1'b0 : 1'b1; localparam ModeW = (XLEN == 32) ? 1 : 4; diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 599af928dd..bc3df48eb4 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -92,7 +92,7 @@ localparam type pte_cva6_t = struct packed { localparam type tlb_update_cva6_t = struct packed { // typedef struct packed { logic valid; // valid flag - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; // + logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; // logic [VPN_LEN-1:0] vpn; // logic [HYP_EXT:0][ASID_LEN-1:0] asid; // pte_cva6_t [HYP_EXT:0] content; @@ -131,10 +131,11 @@ logic itlb_req; assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; +logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0]; +assign lu_vaddr_i[0]=icache_areq_i.fetch_vaddr; cva6_tlb #( .CVA6Cfg (CVA6Cfg), - .HYP_EXT(HYP_EXT), .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -153,8 +154,7 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_gpaddr_o(), + .lu_vaddr_i (lu_vaddr_i), .lu_content_o (itlb_content), .lu_is_page_o(itlb_is_page), @@ -164,7 +164,6 @@ cva6_tlb #( cva6_tlb #( .CVA6Cfg (CVA6Cfg), - .HYP_EXT(HYP_EXT), .TLB_ENTRIES(DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -183,8 +182,7 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i[0]), - .lu_gpaddr_o(), + .lu_vaddr_i (lsu_vaddr_i), .lu_content_o (dtlb_content), .lu_is_page_o(dtlb_is_page), From fb944204c859569421f7fc6afbc5bc699fb01839 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:09:08 +0100 Subject: [PATCH 56/64] Revert "translation parameterized and content in tlb_update_t data type dimensions corrected" This reverts commit 478ff59a5ff314741a57305aa8ab6c52219629d9. --- core/mmu_unify/cva6_shared_tlb.sv | 4 ++-- core/mmu_unify/cva6_tlb.sv | 34 ++++++++++--------------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 06063a8133..49d1c26843 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -239,13 +239,13 @@ module cva6_shared_tlb itlb_update_o.vpn = itlb_vpn_q; itlb_update_o.is_page[0] = shared_tag_rd[i].is_page; itlb_update_o.asid[0] = tlb_update_asid_q; - itlb_update_o.content[0] = pte[i]; + itlb_update_o.content = pte[i]; end else if (dtlb_req_q) begin dtlb_update_o.valid = 1'b1; dtlb_update_o.vpn = dtlb_vpn_q; dtlb_update_o.is_page[0] = shared_tag_rd[i].is_page; dtlb_update_o.asid[0] = tlb_update_asid_q; - dtlb_update_o.content[0] = pte[i]; + dtlb_update_o.content = pte[i]; end end end diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e33d7c27ea..e28fb552fa 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -57,13 +57,13 @@ struct packed { logic [HYP_EXT:0][ASID_LEN-1:0] asid; logic [PT_LEVELS+HYP_EXT-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; + logic [HYP_EXT*2:0] v_st_enbl_i; // v_i,g-stage enabled, s-stage enabled logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; - pte_cva6_t [TLB_ENTRIES-1:0][HYP_EXT:0] content_q , content_n; + pte_cva6_t [HYP_EXT:0][TLB_ENTRIES-1:0] content_q , content_n; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; @@ -72,7 +72,6 @@ logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_level_match; logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy - logic [TLB_ENTRIES-1:0] match_stage; //------------- // Translation //------------- @@ -87,19 +86,15 @@ generate for (i=0; i < TLB_ENTRIES; i++) begin - assign match_stage[i] = tags_q[i].v_st_enbl == v_st_enbl_i; - for(z=0;z>1; //the page size is indicated here - lu_content_o = content_q[i]; + lu_is_page_o = tags_q[i].is_page; //the page size is indicated here + lu_content_o = content_q[0][i]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; end @@ -157,7 +147,6 @@ tags_n[i].asid = tags_q[i].asid; tags_n[i].is_page = tags_q[i].is_page; tags_n[i].valid = tags_q[i].valid; - tags_n[i].v_st_enbl =tags_q[i].v_st_enbl; if (flush_i) begin // invalidate logic // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) @@ -166,10 +155,10 @@ else if (asid_to_be_flushed_is0 && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case) - else if ((!content_q[i][0].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + else if ((!content_q[0][i].g) && (|vaddr_level_match[i]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case) - else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!asid_to_be_flushed_is0)) + else if ((!content_q[0][i].g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!asid_to_be_flushed_is0)) tags_n[i].valid = 1'b0; // normal replacement end else if (update_i.valid & replace_en[i]) begin @@ -177,10 +166,9 @@ tags_n[i].asid = update_i.asid; tags_n[i].is_page= update_i.is_page; tags_n[i].valid = 1'b1; - tags_n[i].v_st_enbl = v_st_enbl_i; // and content as well - content_n[i] = update_i.content; + content_n[0][i] = update_i.content; end end end From d7a3512de3a731e148398da97c7cf3e9e8e4bd4d Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:09:35 +0100 Subject: [PATCH 57/64] Revert "update pte_cva6_t and tlb_update_cva6_t data types to support hypervisor extension. Update all submodules to new data types as first step" This reverts commit 257f6e081b1efb4fd1bd7f5fd7c9158c52c8cf2c. --- core/load_store_unit.sv | 24 +++--------- core/mmu_unify/cva6_mmu.sv | 64 ++++++++++++++----------------- core/mmu_unify/cva6_ptw.sv | 4 +- core/mmu_unify/cva6_shared_tlb.sv | 14 +++---- core/mmu_unify/cva6_tlb.sv | 64 ++++++++++++++----------------- 5 files changed, 72 insertions(+), 98 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 95e622f757..14f523c0a3 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -171,26 +171,15 @@ module load_store_unit // ); if (MMU_PRESENT) begin : gen_mmu - localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; - localparam VPN_LEN = (riscv::XLEN == 64) ? (HYP_EXT ? 29 : 27) : 20; + localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; - localparam int unsigned mmu_ASID_WIDTH [HYP_EXT:0] = {ASID_WIDTH}; - logic [mmu_ASID_WIDTH[0]-1:0] mmu_asid_i [HYP_EXT:0]; - logic [mmu_ASID_WIDTH[0]-1:0] mmu_asid_to_be_flushed_i [HYP_EXT:0]; - logic [riscv::VLEN-1:0] mmu_vaddr_to_be_flushed_i [HYP_EXT:0]; - logic [riscv::VLEN-1:0] mmu_lsu_vaddr_i[HYP_EXT:0]; - - assign mmu_asid_i[0] = asid_i; - assign mmu_asid_to_be_flushed_i[0] =asid_to_be_flushed_i; - assign mmu_vaddr_to_be_flushed_i[0] =vaddr_to_be_flushed_i; - assign mmu_lsu_vaddr_i[0]= mmu_vaddr; cva6_mmu #( .CVA6Cfg (CVA6Cfg), .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), - .ASID_WIDTH (mmu_ASID_WIDTH), + .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) @@ -199,7 +188,7 @@ module load_store_unit .misaligned_ex_i(misaligned_exception), .lsu_is_store_i (st_translation_req), .lsu_req_i (translation_req), - .lsu_vaddr_i (mmu_lsu_vaddr_i), + .lsu_vaddr_i (mmu_vaddr), .lsu_valid_o (translation_valid), .lsu_paddr_o (mmu_paddr), .lsu_exception_o(mmu_exception), @@ -210,14 +199,11 @@ module load_store_unit .req_port_o (dcache_req_ports_o[0]), // icache address translation requests .icache_areq_i (icache_areq_i), - // .asid_to_be_flushed_i, - // .vaddr_to_be_flushed_i, + .asid_to_be_flushed_i, + .vaddr_to_be_flushed_i, .icache_areq_o (icache_areq_o), .pmpcfg_i, .pmpaddr_i, - .asid_i(mmu_asid_i), - .asid_to_be_flushed_i(mmu_asid_to_be_flushed_i), - .vaddr_to_be_flushed_i(mmu_vaddr_to_be_flushed_i), .* ); end else begin : gen_no_mmu diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index bc3df48eb4..cf65db5fc3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -23,11 +23,10 @@ import ariane_pkg::*; parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned INSTR_TLB_ENTRIES = 4, parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH [HYP_EXT:0]= {1}, + parameter int unsigned ASID_WIDTH = 1, parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1, - parameter logic HYP_EXT = 0 + parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, input logic rst_ni, @@ -42,7 +41,7 @@ import ariane_pkg::*; // in the LSU as we distinguish load and stores, what we do here is simple address translation input exception_t misaligned_ex_i, input logic lsu_req_i, // request address translation - input logic [riscv::VLEN-1:0] lsu_vaddr_i[HYP_EXT:0], // virtual address in + input logic [riscv::VLEN-1: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 // Cycle 0 @@ -59,9 +58,9 @@ import ariane_pkg::*; input logic mxr_i, // input logic flag_mprv_i, input logic [riscv::PPNW-1:0] satp_ppn_i, - input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT:0], - input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], + input logic [ASID_WIDTH-1:0] asid_i, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, input logic flush_tlb_i, // Performance counters output logic itlb_miss_o, @@ -92,13 +91,12 @@ localparam type pte_cva6_t = struct packed { localparam type tlb_update_cva6_t = struct packed { // typedef struct packed { logic valid; // valid flag - logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; // + logic [PT_LEVELS-2:0] is_page; // logic [VPN_LEN-1:0] vpn; // - logic [HYP_EXT:0][ASID_LEN-1:0] asid; // - pte_cva6_t [HYP_EXT:0] content; + logic [ASID_LEN-1:0] asid; // + pte_cva6_t content; } ; - logic iaccess_err; // insufficient privilege to access this instruction page logic daccess_err; // insufficient privilege to access this data page logic ptw_active; // PTW is currently walking a page table @@ -112,12 +110,12 @@ logic [riscv::VLEN-1:0] update_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; logic itlb_lu_access; -pte_cva6_t [HYP_EXT:0] itlb_content ; +pte_cva6_t itlb_content; logic [PT_LEVELS-2:0] itlb_is_page; logic itlb_lu_hit; logic dtlb_lu_access; -pte_cva6_t [HYP_EXT:0] dtlb_content; +pte_cva6_t dtlb_content; logic [PT_LEVELS-2:0] dtlb_is_page; logic dtlb_lu_hit; @@ -131,8 +129,6 @@ logic itlb_req; assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; -logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0]; -assign lu_vaddr_i[0]=icache_areq_i.fetch_vaddr; cva6_tlb #( .CVA6Cfg (CVA6Cfg), @@ -154,12 +150,11 @@ cva6_tlb #( .lu_asid_i (asid_i), .asid_to_be_flushed_i (asid_to_be_flushed_i), .vaddr_to_be_flushed_i(vaddr_to_be_flushed_i), - .lu_vaddr_i (lu_vaddr_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), .lu_content_o (itlb_content), .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit), - .v_st_enbl_i(1) + .lu_hit_o (itlb_lu_hit) ); cva6_tlb #( @@ -186,15 +181,14 @@ cva6_tlb #( .lu_content_o (dtlb_content), .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit), - .v_st_enbl_i(1) + .lu_hit_o (dtlb_lu_hit) ); cva6_shared_tlb #( .CVA6Cfg (CVA6Cfg), .SHARED_TLB_DEPTH(64), .SHARED_TLB_WAYS (2), - .ASID_WIDTH (ASID_WIDTH[0]), + .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), @@ -208,7 +202,7 @@ cva6_shared_tlb #( .enable_translation_i (enable_translation_i), .en_ld_st_translation_i(en_ld_st_translation_i), - .asid_i (asid_i[0]), + .asid_i (asid_i), // from TLBs // did we miss? .itlb_access_i(itlb_lu_access), @@ -217,7 +211,7 @@ cva6_shared_tlb #( .dtlb_access_i(dtlb_lu_access), .dtlb_hit_i (dtlb_lu_hit), - .dtlb_vaddr_i (lsu_vaddr_i[0]), + .dtlb_vaddr_i (lsu_vaddr_i), // to TLBs, update logic .itlb_update_o(update_itlb), @@ -238,7 +232,7 @@ cva6_shared_tlb #( cva6_ptw #( .CVA6Cfg (CVA6Cfg), - .ASID_WIDTH(ASID_WIDTH[0]), + .ASID_WIDTH(ASID_WIDTH), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), .pte_cva6_t(pte_cva6_t), @@ -263,7 +257,7 @@ cva6_ptw #( .update_vaddr_o(update_vaddr), - .asid_i(asid_i[0]), + .asid_i(asid_i), // from shared TLB // did we miss? @@ -318,8 +312,8 @@ localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; assign icache_areq_o.fetch_paddr [11:0] = icache_areq_i.fetch_vaddr[11:0]; assign icache_areq_o.fetch_paddr [riscv::PLEN-1:PPNWMin+1] = // (enable_translation_i) ? // - itlb_content[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - (riscv::PLEN-PPNWMin-1)'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + itlb_content.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + riscv::PLEN'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); genvar a; generate @@ -327,7 +321,7 @@ generate for (a=0; a < PT_LEVELS-1; a++) begin assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // (enable_translation_i && (|itlb_is_page[a:0]==0)) ? // - itlb_content[0].ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // + itlb_content.ppn [(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a))-1):(riscv::PPNW - (riscv::PLEN - PPNWMin-1)-((VPN_LEN/PT_LEVELS)*(a+1)))] : // icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; end @@ -342,8 +336,8 @@ always_comb begin : instr_interface // 2. We got an access error because of insufficient permissions -> throw an access exception icache_areq_o.fetch_exception = '0; // Check whether we are allowed to access this memory region from a fetch perspective - iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u)); // 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. @@ -450,11 +444,11 @@ assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // dtlb_pte_q.ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + (riscv::PLEN-PPNWMin)'(lsu_vaddr_q[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); assign lsu_dtlb_ppn_o [11:0] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // - dtlb_content[0].ppn[11:0] : // + dtlb_content.ppn[11:0] : // lsu_vaddr_n[23:12]; genvar i; @@ -468,14 +462,14 @@ genvar i; assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] : // + dtlb_content.ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] : // (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? lsu_vaddr_n[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN -1 : (24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-1) ) -1): (riscv::PLEN > riscv::VLEN) ? 24 :24 + (VPN_LEN/PT_LEVELS)*(PT_LEVELS-i-2)])); end if(riscv::IS_XLEN64) begin assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i && !misaligned_ex_q.valid) ? - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] : + dtlb_content.ppn[riscv::PPNW-1:PPNWMin+1] : lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; end @@ -483,7 +477,7 @@ genvar i; // The data interface is simpler and only consists of a request/response interface always_comb begin : data_interface // save request and DTLB response - lsu_vaddr_n = lsu_vaddr_i[0]; + lsu_vaddr_n = lsu_vaddr_i; lsu_req_n = lsu_req_i; misaligned_ex_n = misaligned_ex_i; dtlb_pte_n = dtlb_content; diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 425e12e679..e7457c34a0 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -141,7 +141,7 @@ genvar x; for (x=0; x < PT_LEVELS-1; x++) begin // update the correct page table level - assign shared_tlb_update_o.is_page[0][x] = (ptw_lvl_q == (x)); + assign shared_tlb_update_o.is_page[x] = (ptw_lvl_q == (x)); // check if the ppn is correctly aligned: // 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault @@ -157,7 +157,7 @@ genvar x; // output the correct ASID - assign shared_tlb_update_o.asid[0] = tlb_update_asid_q; + assign shared_tlb_update_o.asid = tlb_update_asid_q; // set the global mapping bit assign shared_tlb_update_o.content = pte | (global_mapping_q << 5); diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 49d1c26843..e26b7f63ff 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -237,14 +237,14 @@ module cva6_shared_tlb if (itlb_req_q) begin itlb_update_o.valid = 1'b1; itlb_update_o.vpn = itlb_vpn_q; - itlb_update_o.is_page[0] = shared_tag_rd[i].is_page; - itlb_update_o.asid[0] = tlb_update_asid_q; + itlb_update_o.is_page = shared_tag_rd[i].is_page; + itlb_update_o.asid = tlb_update_asid_q; itlb_update_o.content = pte[i]; end else if (dtlb_req_q) begin dtlb_update_o.valid = 1'b1; dtlb_update_o.vpn = dtlb_vpn_q; - dtlb_update_o.is_page[0] = shared_tag_rd[i].is_page; - dtlb_update_o.asid[0] = tlb_update_asid_q; + dtlb_update_o.is_page = shared_tag_rd[i].is_page; + dtlb_update_o.asid = tlb_update_asid_q; dtlb_update_o.content = pte[i]; end end @@ -300,10 +300,10 @@ module cva6_shared_tlb end end //update_flush - assign shared_tag_wr.asid = shared_tlb_update_i.asid[0]; + assign shared_tag_wr.asid = shared_tlb_update_i.asid; // assign shared_tag_wr.vpn[1] = shared_tlb_update_i.vpn[19:10]; // assign shared_tag_wr.vpn[0] = shared_tlb_update_i.vpn[9:0]; - assign shared_tag_wr.is_page = shared_tlb_update_i.is_page[0]; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; genvar z; @@ -318,7 +318,7 @@ module cva6_shared_tlb assign tag_wr_data = shared_tag_wr; assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; - assign pte_wr_data = shared_tlb_update_i.content[0]; + assign pte_wr_data = shared_tlb_update_i.content; assign way_valid = shared_tag_valid_q[shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]]; assign repl_way = (all_ways_valid) ? rnd_way : inv_way; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e28fb552fa..60f8d2bc83 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -12,15 +12,15 @@ // Date: 20/11/2023 // // Description: Translation Lookaside Buffer, parameterizable to Sv32 or Sv39 , -// or sv39x4 fully set-associative +// fully set-associative // This module is an merge of the Sv32 TLB developed by Sebastien -// Jacq (Thales Research & Technology), the Sv39 TLB developed -// by Florian Zaruba and David Schaffenrath and the Sv39x4 by Bruno Sá. +// Jacq (Thales Research & Technology) and the Sv39 TLB developed +// by Florian Zaruba and David Schaffenrath. // // =========================================================================== // // Revisions : // Date Version Author Description -// 2023-12-13 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support +// 2023-11-20 0.1 A.Gonzalez Generic TLB for CVA6 // =========================================================================== // module cva6_tlb @@ -29,43 +29,39 @@ parameter type pte_cva6_t = logic, parameter type tlb_update_cva6_t = logic, parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter logic HYP_EXT = 0, parameter int unsigned TLB_ENTRIES = 4, - parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - parameter int unsigned ASID_LEN[HYP_EXT:0] = 1, //[vmid_len,asid_len] + parameter int unsigned ASID_WIDTH = 1, + parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low - input logic [HYP_EXT*2:0] flush_i, // Flush signal [g_stage,vs stage, normal translation signal] - input logic [HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled + input logic flush_i, // Flush signal // Update TLB input tlb_update_cva6_t update_i, // Lookup signals input logic lu_access_i, - input logic [ASID_WIDTH[0]-1:0] lu_asid_i [HYP_EXT:0], //[lu_vmid,lu_asid] - input logic [riscv::VLEN-1:0] lu_vaddr_i [HYP_EXT:0], //[gp_addr,vaddr] - output pte_cva6_t [HYP_EXT:0] lu_content_o , - input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i [HYP_EXT:0], //[vmid,asid] - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i [HYP_EXT:0], // [gpaddr,vaddr] + input logic [ASID_WIDTH-1:0] lu_asid_i, + input logic [riscv::VLEN-1:0] lu_vaddr_i, + output pte_cva6_t lu_content_o, + input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i, output logic [PT_LEVELS-2:0] lu_is_page_o, output logic lu_hit_o ); // Sv32 defines two levels of page tables, Sv39 defines 3 struct packed { - logic [HYP_EXT:0][ASID_LEN-1:0] asid; - logic [PT_LEVELS+HYP_EXT-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [HYP_EXT:0][PT_LEVELS-2:0] is_page; - logic [HYP_EXT*2:0] v_st_enbl_i; // v_i,g-stage enabled, s-stage enabled + logic [ASID_LEN-1:0] asid; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; - pte_cva6_t [HYP_EXT:0][TLB_ENTRIES-1:0] content_q , content_n; + pte_cva6_t [TLB_ENTRIES-1:0] content_q, content_n; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vpn_match; - logic [TLB_ENTRIES-1:0][HYP_EXT:0] asid_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] level_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] vaddr_vpn_match; @@ -82,23 +78,21 @@ //AND the page_match is also set //At level 0 the page match is always set, so this level will have a match //if all vpn levels match - genvar i,x,z; + genvar i,x; generate for (i=0; i < TLB_ENTRIES; i++) begin - - for(z=0;z= 1) + assert (ASID_WIDTH >= 1) else begin $error("ASID width must be at least 1"); $stop(); From df50be20195ab6004fa4d6e9981e2b0091db4d66 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:13:26 +0100 Subject: [PATCH 58/64] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/load_store_unit.sv | 6 +++--- core/mmu_unify/cva6_shared_tlb.sv | 24 +++++++++++----------- core/mmu_unify/cva6_tlb.sv | 34 +++++++++++++++---------------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 14f523c0a3..ce9b089958 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -171,9 +171,9 @@ module load_store_unit // ); if (MMU_PRESENT) begin : gen_mmu - localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; - localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; - localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; + localparam VPN_LEN = (riscv::XLEN == 64) ? 27 : 20; + localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; cva6_mmu #( .CVA6Cfg (CVA6Cfg), diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index e26b7f63ff..44e5ed146d 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -77,8 +77,8 @@ module cva6_shared_tlb endfunction typedef struct packed { - logic [ASID_LEN-1:0] asid; - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [ASID_LEN-1:0] asid; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; logic [PT_LEVELS-2:0] is_page; } shared_tag_t; @@ -113,7 +113,7 @@ module cva6_shared_tlb logic [ SHARED_TLB_WAYS-1:0] pte_we; logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_addr; - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn_d,vpn_q; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn_d, vpn_q; logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] vpn_match; logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] page_match; logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] level_match; @@ -147,10 +147,10 @@ module cva6_shared_tlb assign itlb_req_o = itlb_req_q; - genvar i,x; - generate - for (i=0; i < SHARED_TLB_WAYS; i++) begin - //identify page_match for all TLB Entries + genvar i, x; + generate + for (i = 0; i < SHARED_TLB_WAYS; i++) begin + //identify page_match for all TLB Entries for (x=0; x < PT_LEVELS; x++) begin assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; @@ -307,11 +307,11 @@ module cva6_shared_tlb genvar z; - generate - for (z=0; z < PT_LEVELS; z++) begin - assign shared_tag_wr.vpn[z] = shared_tlb_update_i.vpn[((VPN_LEN/PT_LEVELS)*(z+1))-1:((VPN_LEN/PT_LEVELS)*z)]; - end - endgenerate + generate + for (z = 0; z < PT_LEVELS; z++) begin + assign shared_tag_wr.vpn[z] = shared_tlb_update_i.vpn[((VPN_LEN/PT_LEVELS)*(z+1))-1:((VPN_LEN/PT_LEVELS)*z)]; + end + endgenerate assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 60f8d2bc83..bd60bd3b17 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -23,7 +23,7 @@ // 2023-11-20 0.1 A.Gonzalez Generic TLB for CVA6 // =========================================================================== // - module cva6_tlb +module cva6_tlb import ariane_pkg::*; #( parameter type pte_cva6_t = logic, @@ -34,7 +34,7 @@ parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 - ) ( +) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic flush_i, // Flush signal @@ -53,10 +53,10 @@ // Sv32 defines two levels of page tables, Sv39 defines 3 struct packed { - logic [ASID_LEN-1:0] asid; - logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; - logic [PT_LEVELS-2:0] is_page; - logic valid; + logic [ASID_LEN-1:0] asid; + logic [PT_LEVELS-1:0][(VPN_LEN/PT_LEVELS)-1:0] vpn; + logic [PT_LEVELS-2:0] is_page; + logic valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; @@ -72,17 +72,17 @@ // Translation //------------- - //at level 0 make page match always 1 - //build level match vector according to vpn_match and page_match - //a level has a match if all vpn of higher levels and current have a match, - //AND the page_match is also set - //At level 0 the page match is always set, so this level will have a match - //if all vpn levels match - genvar i,x; - generate - for (i=0; i < TLB_ENTRIES; i++) begin - //identify page_match for all TLB Entries - // assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; + //at level 0 make page match always 1 + //build level match vector according to vpn_match and page_match + //a level has a match if all vpn of higher levels and current have a match, + //AND the page_match is also set + //At level 0 the page match is always set, so this level will have a match + //if all vpn levels match + genvar i, x; + generate + for (i = 0; i < TLB_ENTRIES; i++) begin + //identify page_match for all TLB Entries + // assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; for (x=0; x < PT_LEVELS; x++) begin //identify page_match for all TLB Entries From b8f6e770d708bdb17830d7f8e5cf9a9a157d463d Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:14:57 +0100 Subject: [PATCH 59/64] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_tlb.sv | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index bd60bd3b17..b0230bb942 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -84,22 +84,22 @@ module cva6_tlb //identify page_match for all TLB Entries // assign page_match[i] = (tags_q[i].is_page[PT_LEVELS-2:0])*2 +1; - for (x=0; x < PT_LEVELS; x++) begin - //identify page_match for all TLB Entries - assign page_match[i][x] = x==0 ? 1 :tags_q[i].is_page[PT_LEVELS-1-x]; - //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; - //identify if there is a hit at each PT level for all TLB entries - assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; - //identify if virtual address vpn matches at all PT levels for all TLB entries - assign vaddr_vpn_match[i][x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; - //identify if there is a hit at each PT level for all TLB entries - assign vaddr_level_match[i][x]= &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; - //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced - assign tags_n[i].vpn[x] = (!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; - end - end - endgenerate + for (x = 0; x < PT_LEVELS; x++) begin + //identify page_match for all TLB Entries + assign page_match[i][x] = x == 0 ? 1 : tags_q[i].is_page[PT_LEVELS-1-x]; + //identify if vpn matches at all PT levels for all TLB entries + assign vpn_match[i][x] = lu_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //identify if virtual address vpn matches at all PT levels for all TLB entries + assign vaddr_vpn_match[i][x] = vaddr_to_be_flushed_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x]; + //identify if there is a hit at each PT level for all TLB entries + assign vaddr_level_match[i][x] = &vaddr_vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + //update vpn field in tags_n for each TLB when the update is valid and the tag needs to be replaced + assign tags_n[i].vpn[x] = (!flush_i && update_i.valid && replace_en[i]) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; + end + end + endgenerate always_comb begin : translation @@ -107,7 +107,7 @@ module cva6_tlb lu_hit = '{default: 0}; lu_hit_o = 1'b0; lu_content_o = '{default: 0}; - lu_is_page_o = 0; + lu_is_page_o = 0; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // first level match, this may be a page, check the ASID flags as well From ce5a46e75f66aaed5d11b89f50cdef974bf05fbc Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:16:09 +0100 Subject: [PATCH 60/64] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_tlb.sv | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index b0230bb942..6f03c2d2d3 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -115,10 +115,10 @@ module cva6_tlb if (tags_q[i].valid && ((lu_asid_i == tags_q[i].asid[ASID_WIDTH-1:0]) || content_q[i].g)) begin // find if there is a match at any level if (|level_match[i]) begin - lu_is_page_o = tags_q[i].is_page; //the page size is indicated here - lu_content_o = content_q[i]; - lu_hit_o = 1'b1; - lu_hit[i] = 1'b1; + lu_is_page_o = tags_q[i].is_page; //the page size is indicated here + lu_content_o = content_q[i]; + lu_hit_o = 1'b1; + lu_hit[i] = 1'b1; end end end @@ -134,7 +134,6 @@ module cva6_tlb // Update and Flush // ------------------ always_comb begin : update_flush - content_n = content_q; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin @@ -157,9 +156,9 @@ module cva6_tlb // normal replacement end else if (update_i.valid & replace_en[i]) begin // update tag array - tags_n[i].asid = update_i.asid; - tags_n[i].is_page= update_i.is_page; - tags_n[i].valid = 1'b1; + tags_n[i].asid = update_i.asid; + tags_n[i].is_page = update_i.is_page; + tags_n[i].valid = 1'b1; // and content as well content_n[i] = update_i.content; From 254378cdb6827e7a79bce22c4ddc292754acad5c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 19 Dec 2023 10:23:58 +0100 Subject: [PATCH 61/64] fixed linting issues --- core/include/riscv_pkg.sv | 1 - core/load_store_unit.sv | 29 ---- core/mmu_unify/cva6_shared_tlb.sv | 37 +++-- core/mmu_unify/cva6_tlb.sv | 236 +++++++++++++++--------------- 4 files changed, 135 insertions(+), 168 deletions(-) diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 6805c7f647..2a9d919c1a 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -321,7 +321,6 @@ package riscv; logic v; } pte_sv32_t; - // ---------------------- // Exception Cause Codes // ---------------------- diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index ce9b089958..c735752643 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -140,35 +140,6 @@ module load_store_unit // ------------------- // MMU e.g.: TLBs/PTW // ------------------- - // if (MMU_PRESENT && (riscv::XLEN == 64)) begin : gen_mmu_sv39 - // mmu #( - // .CVA6Cfg (CVA6Cfg), - // .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), - // .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), - // .ASID_WIDTH (ASID_WIDTH) - // ) i_cva6_mmu ( - // // 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), - // .lsu_paddr_o (mmu_paddr), - // .lsu_exception_o(mmu_exception), - // .lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request - // .lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request - // // connecting PTW to D$ IF - // .req_port_i (dcache_req_ports_i[0]), - // .req_port_o (dcache_req_ports_o[0]), - // // icache address translation requests - // .icache_areq_i (icache_areq_i), - // .asid_to_be_flushed_i, - // .vaddr_to_be_flushed_i, - // .icache_areq_o (icache_areq_o), - // .pmpcfg_i, - // .pmpaddr_i, - // .* - // ); if (MMU_PRESENT) begin : gen_mmu localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 44e5ed146d..9a79fc3054 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -103,11 +103,11 @@ module cva6_shared_tlb logic [ SHARED_TLB_WAYS-1:0] pte_wr_en; logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr; - logic [$bits(pte_cva6_t)-1:0] pte_wr_data; + logic [ $bits(pte_cva6_t)-1:0] pte_wr_data; logic [ SHARED_TLB_WAYS-1:0] pte_rd_en; logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr; - logic [$bits(pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; + logic [ $bits(pte_cva6_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0]; logic [ SHARED_TLB_WAYS-1:0] pte_req; logic [ SHARED_TLB_WAYS-1:0] pte_we; @@ -152,24 +152,23 @@ module cva6_shared_tlb for (i = 0; i < SHARED_TLB_WAYS; i++) begin //identify page_match for all TLB Entries - for (x=0; x < PT_LEVELS; x++) begin - assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; - // assign vpn_d[x] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] :((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] : vpn_q[x]); - assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; - assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; - end + for (x=0; x < PT_LEVELS; x++) begin + assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; + assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; end - endgenerate - - genvar w; - generate - for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // - itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // - ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? // - dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); - end - endgenerate + end + endgenerate + + genvar w; + generate + for (w=0; w < PT_LEVELS; w++) begin + assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // + ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? // + dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); + end + endgenerate /////////////////////////////////////////////////////// diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 6f03c2d2d3..c06ff17eaa 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -160,149 +160,147 @@ module cva6_tlb tags_n[i].is_page = update_i.is_page; tags_n[i].valid = 1'b1; - // and content as well - content_n[i] = update_i.content; + // and content as well + content_n[i] = update_i.content; + end end end -end -// ----------------------------------------------- -// PLRU - Pseudo Least Recently Used Replacement -// ----------------------------------------------- -logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n; -logic en; -int unsigned idx_base, shift, new_index; -always_comb begin : plru_replacement - plru_tree_n = plru_tree_q; - en = '0; - idx_base = '0; - shift = '0; - new_index = '0; - // The PLRU-tree indexing: - // lvl0 0 - // / \ - // / \ - // lvl1 1 2 - // / \ / \ - // lvl2 3 4 5 6 - // / \ /\/\ /\ - // ... ... ... ... - // Just predefine which nodes will be set/cleared - // E.g. for a TLB with 8 entries, the for-loop is semantically - // equivalent to the following pseudo-code: - // unique case (1'b1) - // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1}; - // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0}; - // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1}; - // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0}; - // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1}; - // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0}; - // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1}; - // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0}; - // default: begin /* No hit */ end - // endcase - for ( - int unsigned i = 0; i < TLB_ENTRIES; i++ - ) begin + // ----------------------------------------------- + // PLRU - Pseudo Least Recently Used Replacement + // ----------------------------------------------- + logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n; + logic en; + int unsigned idx_base, shift, new_index; + always_comb begin : plru_replacement + plru_tree_n = plru_tree_q; + en = '0; + idx_base = '0; + shift = '0; + new_index = '0; + // The PLRU-tree indexing: + // lvl0 0 + // / \ + // / \ + // lvl1 1 2 + // / \ / \ + // lvl2 3 4 5 6 + // / \ /\/\ /\ + // ... ... ... ... + // Just predefine which nodes will be set/cleared + // E.g. for a TLB with 8 entries, the for-loop is semantically + // equivalent to the following pseudo-code: + // unique case (1'b1) + // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1}; + // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0}; + // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1}; + // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0}; + // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1}; + // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0}; + // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1}; + // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0}; + // default: begin /* No hit */ end + // endcase + for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // we got a hit so update the pointer as it was least recently used - if (lu_hit[i] & lu_access_i) begin - // Set the nodes to the values we would expect + if (lu_hit[i] & lu_access_i) begin + // Set the nodes to the values we would expect + for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin + idx_base = $unsigned((2 ** lvl) - 1); + // lvl0 <=> MSB, lvl1 <=> MSB-1, ... + shift = $clog2(TLB_ENTRIES) - lvl; + // to circumvent the 32 bit integer arithmetic assignment + new_index = ~((i >> (shift - 1)) & 32'b1); + plru_tree_n[idx_base+(i>>shift)] = new_index[0]; + end + end + end + // Decode tree to write enable signals + // Next for-loop basically creates the following logic for e.g. an 8 entry + // TLB (note: pseudo-code obviously): + // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1} + // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0} + // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1} + // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0} + // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1} + // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0} + // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1} + // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0} + // For each entry traverse the tree. If every tree-node matches, + // the corresponding bit of the entry's index, this is + // the next entry to replace. + for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin + en = 1'b1; for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin idx_base = $unsigned((2 ** lvl) - 1); // lvl0 <=> MSB, lvl1 <=> MSB-1, ... shift = $clog2(TLB_ENTRIES) - lvl; - // to circumvent the 32 bit integer arithmetic assignment - new_index = ~((i >> (shift - 1)) & 32'b1); - plru_tree_n[idx_base+(i>>shift)] = new_index[0]; + + // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1); + new_index = (i >> (shift - 1)) & 32'b1; + if (new_index[0]) begin + en &= plru_tree_q[idx_base+(i>>shift)]; + end else begin + en &= ~plru_tree_q[idx_base+(i>>shift)]; + end end + replace_en[i] = en; end end - // Decode tree to write enable signals - // Next for-loop basically creates the following logic for e.g. an 8 entry - // TLB (note: pseudo-code obviously): - // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1} - // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0} - // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1} - // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0} - // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1} - // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0} - // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1} - // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0} - // For each entry traverse the tree. If every tree-node matches, - // the corresponding bit of the entry's index, this is - // the next entry to replace. - for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin - en = 1'b1; - for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin - idx_base = $unsigned((2 ** lvl) - 1); - // lvl0 <=> MSB, lvl1 <=> MSB-1, ... - shift = $clog2(TLB_ENTRIES) - lvl; - // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1); - new_index = (i >> (shift - 1)) & 32'b1; - if (new_index[0]) begin - en &= plru_tree_q[idx_base+(i>>shift)]; - end else begin - en &= ~plru_tree_q[idx_base+(i>>shift)]; - end + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + tags_q <= '{default: 0}; + content_q <= '{default: 0}; + plru_tree_q <= '{default: 0}; + end else begin + tags_q <= tags_n; + content_q <= content_n; + plru_tree_q <= plru_tree_n; end - replace_en[i] = en; end -end + //-------------- + // Sanity checks + //-------------- -// sequential process -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - tags_q <= '{default: 0}; - content_q <= '{default: 0}; - plru_tree_q <= '{default: 0}; - end else begin - tags_q <= tags_n; - content_q <= content_n; - plru_tree_q <= plru_tree_n; + //pragma translate_off + `ifndef VERILATOR + + initial begin : p_assertions + assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1)) + else begin + $error("TLB size must be a multiple of 2 and greater than 1"); + $stop(); + end + assert (ASID_WIDTH >= 1) + else begin + $error("ASID width must be at least 1"); + $stop(); + end end -end -//-------------- -// Sanity checks -//-------------- -//pragma translate_off -`ifndef VERILATOR + // Just for checking + function int countSetBits(logic [TLB_ENTRIES-1:0] vector); + automatic int count = 0; + foreach (vector[idx]) begin + count += vector[idx]; + end + return count; + endfunction -initial begin : p_assertions - assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1)) + assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1)) else begin - $error("TLB size must be a multiple of 2 and greater than 1"); + $error("More then one hit in TLB!"); $stop(); end - assert (ASID_WIDTH >= 1) + assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1)) else begin - $error("ASID width must be at least 1"); + $error("More then one TLB entry selected for next replace!"); $stop(); end -end - -// Just for checking -function int countSetBits(logic [TLB_ENTRIES-1:0] vector); - automatic int count = 0; - foreach (vector[idx]) begin - count += vector[idx]; - end - return count; -endfunction - -assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1)) -else begin - $error("More then one hit in TLB!"); - $stop(); -end -assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1)) -else begin - $error("More then one TLB entry selected for next replace!"); - $stop(); -end -`endif -//pragma translate_on + `endif + //pragma translate_on endmodule From a10ba44f48de3ce0e98633888607d8354f85b471 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 19 Dec 2023 10:31:29 +0100 Subject: [PATCH 62/64] more linting issues --- core/mmu_unify/cva6_shared_tlb.sv | 16 ++++++++-------- core/mmu_unify/cva6_tlb.sv | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 9a79fc3054..6bd34a0d0e 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -152,20 +152,20 @@ module cva6_shared_tlb for (i = 0; i < SHARED_TLB_WAYS; i++) begin //identify page_match for all TLB Entries - for (x=0; x < PT_LEVELS; x++) begin - assign page_match[i][x] = x==0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; - assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; - assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; + for (x = 0; x < PT_LEVELS; x++) begin + assign page_match[i][x] = x == 0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; + assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; + assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; end end endgenerate genvar w; generate - for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // - itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // - ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i)? // + for (w = 0; w < PT_LEVELS; w++) begin + assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // + ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) ? // dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); end endgenerate diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index c06ff17eaa..95f0b2b29a 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -264,8 +264,8 @@ module cva6_tlb // Sanity checks //-------------- - //pragma translate_off - `ifndef VERILATOR +//pragma translate_off +`ifndef VERILATOR initial begin : p_assertions assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1)) @@ -300,7 +300,7 @@ module cva6_tlb $stop(); end - `endif - //pragma translate_on +`endif +//pragma translate_on endmodule From 34e30d36504aab6f18ecec6d2ce92f23c8e83aa6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 19 Dec 2023 10:35:14 +0100 Subject: [PATCH 63/64] pragma linting --- core/mmu_unify/cva6_tlb.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 95f0b2b29a..61bbc4754c 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -264,7 +264,7 @@ module cva6_tlb // Sanity checks //-------------- -//pragma translate_off + //pragma translate_off `ifndef VERILATOR initial begin : p_assertions @@ -301,6 +301,6 @@ module cva6_tlb end `endif -//pragma translate_on + //pragma translate_on endmodule From 55681a473e0cac21a41350d003e6b35e85021029 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 19 Dec 2023 10:39:18 +0100 Subject: [PATCH 64/64] linting --- core/mmu_unify/cva6_shared_tlb.sv | 12 ++++++------ core/mmu_unify/cva6_tlb.sv | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 6bd34a0d0e..b39031c7b9 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -153,7 +153,7 @@ module cva6_shared_tlb //identify page_match for all TLB Entries for (x = 0; x < PT_LEVELS; x++) begin - assign page_match[i][x] = x == 0 ? 1 :shared_tag_rd[i].is_page[PT_LEVELS-1-x]; + assign page_match[i][x] = x == 0 ? 1 : shared_tag_rd[i].is_page[PT_LEVELS-1-x]; assign vpn_match[i][x] = vpn_q[x] == shared_tag_rd[i].vpn[x]; assign level_match[i][x] = &vpn_match[i][PT_LEVELS-1:x] & page_match[i][x]; end @@ -162,11 +162,11 @@ module cva6_shared_tlb genvar w; generate - for (w = 0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // - itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // - ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) ? // - dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); + for (w = 0; w < PT_LEVELS; w++) begin + assign vpn_d[w] = (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + itlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : // + ((en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) ? // + dtlb_vaddr_i[12+((VPN_LEN/PT_LEVELS)*(w+1))-1:12+((VPN_LEN/PT_LEVELS)*w)] : vpn_q[w]); end endgenerate diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 61bbc4754c..bc1adba896 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -244,6 +244,7 @@ module cva6_tlb en &= ~plru_tree_q[idx_base+(i>>shift)]; end end + replace_en[i] = en; end end