From 6aa098018683c0959f9d27e9582f0e805fa7a5b6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 20 Nov 2023 12:47:57 +0100 Subject: [PATCH 001/182] 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 002/182] 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 003/182] 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 004/182] 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 005/182] 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 006/182] 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 007/182] 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 008/182] 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 009/182] 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 010/182] 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 011/182] 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 012/182] 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 013/182] 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 014/182] 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 015/182] 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 016/182] 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 017/182] 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 018/182] 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 019/182] 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 020/182] 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 021/182] 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 022/182] 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 023/182] 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 024/182] 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 025/182] 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 026/182] 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 027/182] 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 028/182] 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 029/182] 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 030/182] 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 031/182] 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 032/182] 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 033/182] 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 034/182] 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 035/182] 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 036/182] 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 037/182] 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 038/182] 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 039/182] 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 040/182] 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 041/182] 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 042/182] 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 043/182] 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 044/182] 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 045/182] 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 046/182] 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 047/182] 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 048/182] 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 049/182] 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 050/182] 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 051/182] 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 052/182] 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 053/182] 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 054/182] 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 055/182] 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 056/182] 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 057/182] 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 8a3f58f9701de07662d99c3104ad828839aa4621 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:34:02 +0100 Subject: [PATCH 058/182] Revert "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 d7a3512de3a731e148398da97c7cf3e9e8e4bd4d. --- 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 89e11c9a608fe10b04444ee835eccc72a504aed2 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:34:20 +0100 Subject: [PATCH 059/182] Revert "Revert "translation parameterized and content in tlb_update_t data type dimensions corrected"" This reverts commit fb944204c859569421f7fc6afbc5bc699fb01839. --- 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 79fc1d0474b80ab58d3b6cdfaef6a3f7fd4a0e40 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:34:46 +0100 Subject: [PATCH 060/182] Revert "Revert "complete tlb merge for sv39x4 v0"" This reverts commit 6d8b0c32f982c554968d0aafadaa058fb4bc3bf1. --- 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 8fc35ff895779173c12b10dffc29c8c42499e78c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 15 Dec 2023 17:34:59 +0100 Subject: [PATCH 061/182] Revert "Revert "common tlb with hypervisor support"" This reverts commit 8e826b212b4c6724a6395444a5c3952416f14ede. --- 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: Mon, 18 Dec 2023 11:21:59 +0100 Subject: [PATCH 062/182] fix some tlb issues --- core/mmu_unify/cva6_mmu.sv | 5 +- core/mmu_unify/cva6_ptw.sv | 2 +- core/mmu_unify/cva6_tlb.sv | 100 ++++++++++++++++++++++--------------- 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 599af928dd..9c472a5a55 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -23,11 +23,12 @@ 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 logic HYP_EXT = 0, 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 logic HYP_EXT = 0 + parameter int unsigned PT_LEVELS = 1 + ) ( input logic clk_i, input logic rst_ni, diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 425e12e679..7ca8610afb 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -159,7 +159,7 @@ genvar x; // output the correct ASID 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); + assign shared_tlb_update_o.content[0] = pte | (global_mapping_q << 5); assign req_port_o.tag_valid = tag_valid_q; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 172dabc26a..12ba9f49c2 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -122,20 +122,20 @@ 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[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; + // 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 @@ -155,10 +155,28 @@ // 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 && (&asid_match[i] || (v_st_enbl_i[0] && content_q[i][0].g)) && match_stage[i]) begin + + if(HYP_EXT) begin + 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; + 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; + 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 + + + // find if there is a match at any level if (|level_match[i]) begin lu_is_page_o = page_match[i] >>1; //the page size is indicated here - lu_content_o = content_q[i]; + lu_content_o[0] = content_q[i][0]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; // Compute G-Stage PPN based on the gpaddr @@ -168,6 +186,7 @@ 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]; + lu_content_o[1] = page_match[1] ? content_q[i][1] : g_content; end end end @@ -201,38 +220,41 @@ 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 (HYP_EXT) begin + + // computes the final gppn based on the guest physical address + + if ( tags_q[i].v_st_enbl[0]) begin + gppn[i] = content_q[i][0].ppn[(riscv::GPPNW-1):0]; + if(tags_q[i].is_page[1][0]) + gppn[i][8:0] = tags_q[i].vpn[0]; + if(tags_q[i].is_page[0][0]) + gppn[i][17:0] = {tags_q[i].vpn[1],tags_q[i].vpn[0]}; + end else begin + gppn[i] = {tags_q[i].vpn[3],tags_q[i].vpn[2],tags_q[i].vpn[1],tags_q[i].vpn[0]}; + end + + gpaddr_gppn0_match[i] = (vaddr_to_be_flushed_i[1][20:12] == gppn[i][8:0]); + gpaddr_gppn1_match[i] = (vaddr_to_be_flushed_i[1][29:21] == gppn[i][17:9]); + gpaddr_gppn2_match[i] = (vaddr_to_be_flushed_i[1][30+riscv::GPPN2:30] == gppn[i][18+riscv::GPPN2:18]); + end + if ((flush_i[0] & (!HYP_EXT || (HYP_EXT && !tags_q[i].v_st_enbl[HYP_EXT*2])))) 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; + if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0]) + 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)) + else if (asid_to_be_flushed_is0[0] && (|vaddr_level_match[i]) && (~vaddr_to_be_flushed_is0[0])) 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[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[0]) && (!asid_to_be_flushed_is0[0])) 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[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) && (!asid_to_be_flushed_is0[0])) tags_n[i].valid = 1'b0; - end else if (HYP_EXT) begin - - // computes the final gppn based on the guest physical address - - if ( tags_q[i].v_st_enbl[0]) begin - gppn[i] = content_q[i][0].ppn[(riscv::GPPNW-1):0]; - if(tags_q[i].is_page[1][0]) - gppn[i][8:0] = tags_q[i].vpn[0]; - if(tags_q[i].is_page[0][0]) - gppn[i][17:0] = {tags_q[i].vpn[1],tags_q[i].vpn[0]}; - end else begin - gppn[i] = {tags_q[i].vpn[3],tags_q[i].vpn[2],tags_q[i].vpn[1],tags_q[i].vpn[0]}; - end - - gpaddr_gppn0_match[i] = (vaddr_to_be_flushed_i[1][20:12] == gppn[i][8:0]); - gpaddr_gppn1_match[i] = (vaddr_to_be_flushed_i[1][29:21] == gppn[i][17:9]); - gpaddr_gppn2_match[i] = (vaddr_to_be_flushed_i[1][30+riscv::GPPN2:30] == gppn[i][18+riscv::GPPN2:18]); - - if (flush_i[1]) begin + end else if (HYP_EXT && flush_i[HYP_EXT]) begin if(tags_q[i].v_st_enbl[HYP_EXT*2] && tags_q[i].v_st_enbl[0]) begin // invalidate logic // flush everything if current VMID matches and ASID is 0 and vaddr is 0 ("SFENCE.VMA/HFENCE.VVMA x0 x0" case) @@ -248,7 +270,7 @@ else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0] && ((tags_q[i].v_st_enbl[1] && lu_asid_i[1] == tags_q[i].asid[1]) || !tags_q[i].v_st_enbl[1])) && (!asid_to_be_flushed_is0[0])) tags_n[i].valid = 1'b0; end - end else if (flush_i[2]) begin + end else if (HYP_EXT && flush_i[HYP_EXT*2]) begin if(tags_q[i].v_st_enbl[1]) begin // invalidate logic // flush everything if vmid is 0 and addr is 0 ("HFENCE.GVMA x0 x0" case) @@ -263,9 +285,7 @@ // the entry is flushed if the vmid matches and gpaddr is 0. ("HFENCE.GVMA 0 vmid" case) else if ((vaddr_to_be_flushed_is0[1]) && (asid_to_be_flushed_i[1] == tags_q[i].asid[1]) && (!asid_to_be_flushed_is0[1])) tags_n[i].valid = 1'b0; - end - end - + end // normal replacement end else if (update_i.valid & replace_en[i]) begin From 4019f32579abdefe1e64f4c5eca07eb9e55bc6ab Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 18 Dec 2023 13:50:37 +0100 Subject: [PATCH 063/182] fix assignment of page structure --- core/mmu_unify/cva6_ptw.sv | 2 +- core/mmu_unify/cva6_shared_tlb.sv | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 7ca8610afb..d528d9eeda 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][0] = (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 diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 06063a8133..5c0267c7c0 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -79,7 +79,7 @@ module cva6_shared_tlb 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; + logic [PT_LEVELS-2:0][0:0] is_page; } shared_tag_t; shared_tag_t shared_tag_wr; @@ -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][0]; // 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]; @@ -237,13 +237,13 @@ 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.is_page = shared_tag_rd[i].is_page; itlb_update_o.asid[0] = tlb_update_asid_q; 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.is_page = shared_tag_rd[i].is_page; dtlb_update_o.asid[0] = tlb_update_asid_q; dtlb_update_o.content[0] = pte[i]; end @@ -303,7 +303,7 @@ module cva6_shared_tlb 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[0]; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; genvar z; From 1475404fe04457ec79700c85be72c15c9d552799 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 18 Dec 2023 13:51:03 +0100 Subject: [PATCH 064/182] correct assignment of vaddr in HYP extension --- core/mmu_unify/cva6_tlb.sv | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 12ba9f49c2..7d1b2fad98 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -95,7 +95,7 @@ assign match_stage[i] = tags_q[i].v_st_enbl == v_st_enbl_i; for(z=0;z Date: Tue, 19 Dec 2023 18:20:58 +0100 Subject: [PATCH 065/182] merge 1lb with hypervisor, sv39 boots ok --- core/mmu_unify/cva6_mmu.sv | 51 +- core/mmu_unify/cva6_ptw.sv | 740 +++++++++++++++--------------- core/mmu_unify/cva6_shared_tlb.sv | 614 ++++++++++++------------- core/mmu_unify/cva6_tlb.sv | 19 +- 4 files changed, 733 insertions(+), 691 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 9c472a5a55..1109f084df 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -28,7 +28,6 @@ import ariane_pkg::*; 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, @@ -99,6 +98,15 @@ localparam type tlb_update_cva6_t = struct packed { pte_cva6_t [HYP_EXT:0] content; } ; +localparam type tlb_update_cva6_t1 = 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 @@ -111,6 +119,7 @@ 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; +tlb_update_cva6_t1 update_itlb1, update_dtlb1, update_shared_tlb1; logic itlb_lu_access; pte_cva6_t [HYP_EXT:0] itlb_content ; @@ -132,6 +141,34 @@ logic itlb_req; assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; +assign update_shared_tlb.valid = update_shared_tlb1.valid; +assign update_shared_tlb.vpn = update_shared_tlb1.vpn; +assign update_shared_tlb.asid[0] = update_shared_tlb1.asid; +assign update_shared_tlb.content[0] = update_shared_tlb1.content; + +assign update_itlb.valid = update_itlb1.valid; +assign update_itlb.vpn = update_itlb1.vpn; +assign update_itlb.asid[0] = update_itlb1.asid; +assign update_itlb.content[0] = update_itlb1.content; + +assign update_dtlb.valid = update_dtlb1.valid; +assign update_dtlb.vpn = update_dtlb1.vpn; +assign update_dtlb.asid[0] = update_dtlb1.asid; +assign update_dtlb.content[0] = update_dtlb1.content; + + +genvar x; + generate + for (x=0; x < PT_LEVELS-1; x++) begin + assign update_shared_tlb.is_page[x][0] = update_shared_tlb1.is_page[x]; + assign update_itlb.is_page[x][0] = update_itlb1.is_page[x]; + assign update_dtlb.is_page[x][0] = update_dtlb1.is_page[x]; + end + endgenerate + + + + cva6_tlb #( .CVA6Cfg (CVA6Cfg), @@ -202,7 +239,7 @@ cva6_shared_tlb #( .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t) + .tlb_update_cva6_t(tlb_update_cva6_t1) ) i_shared_tlb ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -223,8 +260,8 @@ cva6_shared_tlb #( .dtlb_vaddr_i (lsu_vaddr_i[0]), // to TLBs, update logic - .itlb_update_o(update_itlb), - .dtlb_update_o(update_dtlb), + .itlb_update_o(update_itlb1), + .dtlb_update_o(update_dtlb1), // Performance counters .itlb_miss_o(itlb_miss_o), @@ -245,7 +282,7 @@ cva6_ptw #( .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS), .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t) + .tlb_update_cva6_t(tlb_update_cva6_t1) ) i_ptw ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -262,7 +299,7 @@ cva6_ptw #( .req_port_o (req_port_o), // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), + .shared_tlb_update_o(update_shared_tlb1), .update_vaddr_o(update_vaddr), @@ -489,7 +526,7 @@ always_comb begin : data_interface lsu_vaddr_n = lsu_vaddr_i[0]; lsu_req_n = lsu_req_i; misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content; + dtlb_pte_n = dtlb_content[0]; dtlb_hit_n = dtlb_lu_hit; lsu_is_store_n = lsu_is_store_i; dtlb_is_page_n = dtlb_is_page; diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index d528d9eeda..e43fe442f5 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -26,401 +26,401 @@ /* verilator lint_off WIDTH */ module cva6_ptw - import ariane_pkg::*; +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, - 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 + 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, + 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; - - pte_cva6_t pte; - assign pte = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); - - - 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] 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; - 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]; +// input registers +logic data_rvalid_q; +riscv::xlen_t data_rdata_q; + +pte_cva6_t pte; +assign pte = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); + + +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] 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; +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 + generate + for (x=0; x < PT_LEVELS-1; x++) begin - // update the correct page table level - assign shared_tlb_update_o.is_page[x][0] = (ptw_lvl_q == (x)); + // update the correct page table level + 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)) && (pte.ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-x)-1:0] != '0); + // 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)) && (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[0] = tlb_update_asid_q; - // set the global mapping bit - assign shared_tlb_update_o.content[0] = 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 = 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 - //------------------- - // 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'(PT_LEVELS); - 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 = 0; - 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 + //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 - 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 + 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 = 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 +//------------------- +// 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'(PT_LEVELS); + 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 = 0; + 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; + 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 - // ------------ - // 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 + state_d = PROPAGATE_ERROR; end - //if there is a misaligned page, propagate error - if (|misaligned_page) begin - state_d = PROPAGATE_ERROR; + // 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 - // 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-1) begin - // Should already be the last level page table => Error - 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], (PT_LEVELS)'(0)}; - state_d = WAIT_GRANT; - // end - 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-1) begin + // Should already be the last level page table => Error + 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], (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 + // 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 - // 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; + // we've got a data WAIT_GRANT so tell the cache that the tag is valid end - end - - //for simulation purposes - // 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 <= 0; - 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; + // 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 + +//for simulation purposes +// 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 <= 0; + 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 */ +/* verilator lint_on WIDTH */ \ No newline at end of file diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 5c0267c7c0..8787104d79 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -18,381 +18,381 @@ /* verilator lint_off WIDTH */ module cva6_shared_tlb - import ariane_pkg::*; +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, - parameter int ASID_WIDTH = 1, - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + 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, + parameter int 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, + 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 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, + 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, + // 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, + 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, + // 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, + // 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 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, + output logic itlb_req_o, - // Update shared TLB in case of miss - input tlb_update_cva6_t shared_tlb_update_i + // 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][0:0] is_page; - } shared_tag_t; +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; +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(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(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 [ 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 [ 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 [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; +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; - 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; +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; - 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][0]; - // 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_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 - 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 - /////////////////////////////////////////////////////// - // tag comparison, hit generation - /////////////////////////////////////////////////////// - always_comb begin : itlb_dtlb_miss - itlb_miss_o = 1'b0; - dtlb_miss_o = 1'b0; - tag_rd_en = '0; - pte_rd_en = '0; +/////////////////////////////////////////////////////// +// tag comparison, hit generation +/////////////////////////////////////////////////////// +always_comb begin : itlb_dtlb_miss + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; - itlb_req_d = 1'b0; - dtlb_req_d = 1'b0; + tag_rd_en = '0; + pte_rd_en = '0; - tlb_update_asid_d = tlb_update_asid_q; + itlb_req_d = 1'b0; + dtlb_req_d = 1'b0; - shared_tlb_access_d = '0; - shared_tlb_vaddr_d = shared_tlb_vaddr_q; + tlb_update_asid_d = tlb_update_asid_q; - tag_rd_addr = '0; - pte_rd_addr = '0; + shared_tlb_access_d = '0; + shared_tlb_vaddr_d = shared_tlb_vaddr_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)]; + 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; - dtlb_miss_o = 1'b1; - dtlb_req_d = 1'b1; + // 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)]; - tlb_update_asid_d = asid_i; + dtlb_miss_o = 1'b1; + dtlb_req_d = 1'b1; - shared_tlb_access_d = 1'b1; - shared_tlb_vaddr_d = dtlb_vaddr_i; - end - end //itlb_dtlb_miss + tlb_update_asid_d = asid_i; - 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; - itlb_update_o.vpn = itlb_vpn_q; - itlb_update_o.is_page = shared_tag_rd[i].is_page; - itlb_update_o.asid[0] = tlb_update_asid_q; - 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 = shared_tag_rd[i].is_page; - dtlb_update_o.asid[0] = tlb_update_asid_q; - dtlb_update_o.content[0] = pte[i]; - end + 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; + 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 //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 //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 //update_flush + end +end //update_flush - 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.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; - 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 +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 - 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[0]; +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) - ); +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) +); - 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]) ); - /////////////////////////////////////////////////////// - // 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(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] = pte_cva6_t'(pte_rd_data[i]); - end + assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]); + + // PTE RAM + sram #( + .DATA_WIDTH($bits(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] = pte_cva6_t'(pte_rd_data[i]); +end endmodule -/* verilator lint_on WIDTH */ +/* verilator lint_on WIDTH */ \ No newline at end of file diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 7d1b2fad98..5a6a17f2bc 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -78,6 +78,7 @@ 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; + logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; //------------- // Translation //------------- @@ -88,7 +89,7 @@ //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,z,w; generate for (i=0; i < TLB_ENTRIES; i++) begin @@ -100,11 +101,11 @@ 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)? // + assign page_match[i][x] = x==0 ? 1 :(HYP_EXT && x==(PT_LEVELS-2)? // ((&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][0] && (tags_q[i].is_page[PT_LEVELS-2-x][1] || tags_q[i].is_page[PT_LEVELS-1-x][1])) // + || (tags_q[i].is_page[PT_LEVELS-1-x][1] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): + tags_q[i].is_page[PT_LEVELS-1-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]) ? // @@ -113,7 +114,7 @@ //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] = (HYP_EXT && x==(PT_LEVELS-1)? // + assign vaddr_vpn_match[i][x] = (HYP_EXT && x==(PT_LEVELS-1))? // vaddr_to_be_flushed_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[x] && vaddr_to_be_flushed_i[0][VPN_LEN-1: VPN_LEN-VPN_LEN%PT_LEVELS] == tags_q[i].vpn[x+1][VPN_LEN%PT_LEVELS-1:0]: // vaddr_to_be_flushed_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 @@ -122,6 +123,10 @@ 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 + for (w=0; w < PT_LEVELS - 1; w++) begin + assign is_page_o[i][w] = page_match[i][PT_LEVELS - 1 - w]; + end + if(HYP_EXT) begin assign tags_n[i].vpn[PT_LEVELS+HYP_EXT-1][VPN_LEN%PT_LEVELS-1:0] =(~flush_i[0] && 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]) ? @@ -177,7 +182,7 @@ // find if there is a match at any level if (|level_match[i]) begin - lu_is_page_o = page_match[i] >>1; //the page size is indicated here + lu_is_page_o = is_page_o[i]; //the page size is indicated here lu_content_o[0] = content_q[i][0]; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; From 6890acb45670e3380af937ff1a8c83b4b126b43a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 19 Dec 2023 19:35:57 +0100 Subject: [PATCH 066/182] common mmu top interface and instantiation in load store unit --- core/load_store_unit.sv | 9 +- core/mmu_unify/cva6_mmu.sv | 166 +++++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 63 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 95e622f757..e630763537 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -199,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_lsu_vaddr_i), + .lsu_vaddr_i (mmu_vaddr), .lsu_valid_o (translation_valid), .lsu_paddr_o (mmu_paddr), .lsu_exception_o(mmu_exception), @@ -218,6 +218,13 @@ module load_store_unit .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), + .lsu_tinst_i(0), + .csr_hs_ld_st_inst_o(), + .v_i(1), + .ld_st_v_i(0), + .hlvx_inst_i ( 0 ), + .hs_ld_st_inst_i ( 0 ), + .* ); end else begin : gen_no_mmu diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1109f084df..6c9b7f3b3b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -32,37 +32,43 @@ import ariane_pkg::*; 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 + input logic [HYP_EXT:0] enable_translation_i, + input logic [HYP_EXT:0] 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 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 riscv::xlen_t lsu_tinst_i, // transformed instruction in input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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) + 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 + 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 riscv::priv_lvl_t priv_lvl_i, + input logic v_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic ld_st_v_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i, + input logic [riscv::PPNW-1:0] satp_ppn_i [HYP_EXT*2:0], 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, + input logic [HYP_EXT*2:0] flush_tlb_i, // Performance counters output logic itlb_miss_o, output logic dtlb_miss_o, @@ -108,7 +114,7 @@ localparam type tlb_update_cva6_t1 = struct packed { } ; -logic iaccess_err; // insufficient privilege to access this instruction page +logic [HYP_EXT:0] 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 @@ -136,6 +142,7 @@ logic [riscv::VLEN-1:0] shared_tlb_vaddr; logic shared_tlb_hit; logic itlb_req; +logic [HYP_EXT*2:0] v_st_enbl_i // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; @@ -166,6 +173,11 @@ genvar x; end endgenerate + v_st_enbl_i[0]=v_i; + + if(HYP_EXT) begin + v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; + end @@ -197,7 +209,7 @@ cva6_tlb #( .lu_is_page_o(itlb_is_page), .lu_hit_o (itlb_lu_hit), - .v_st_enbl_i(1) + .v_st_enbl_i(v_st_enbl_i) ); cva6_tlb #( @@ -221,13 +233,13 @@ 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_vaddr_i (lsu_vaddr_i), .lu_gpaddr_o(), .lu_content_o (dtlb_content), .lu_is_page_o(dtlb_is_page), .lu_hit_o (dtlb_lu_hit), - .v_st_enbl_i(1) + .v_st_enbl_i(v_st_enbl_i) ); cva6_shared_tlb #( @@ -245,8 +257,8 @@ cva6_shared_tlb #( .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), + .enable_translation_i (enable_translation_i[0]), + .en_ld_st_translation_i(en_ld_st_translation_i[0]), .asid_i (asid_i[0]), // from TLBs @@ -257,7 +269,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_itlb1), @@ -314,8 +326,8 @@ cva6_ptw #( .itlb_req_i(itlb_req), // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), + .satp_ppn_i(satp_ppn_i[0]), // ppn from satp + .mxr_i (mxr_i[0]), // Performance counters .shared_tlb_miss_o(), //open for now @@ -357,7 +369,7 @@ 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) ? // + ([enable_translation_i[0]]) ? // 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]); @@ -366,7 +378,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)) ? // + (enable_translation_i[0] && (|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)))] : // icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; end @@ -382,22 +394,25 @@ 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) + iaccess_err[0] = icache_areq_i.fetch_req && enable_translation_i[0]&& (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + iaccess_err[1] = icache_areq_i.fetch_req && enable_translation_i[1] && !itlb_content[1].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 (|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_exception.cause = riscv::INSTR_ACCESS_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end icache_areq_o.fetch_valid = 1'b0; @@ -408,17 +423,25 @@ always_comb begin : instr_interface if (itlb_lu_hit) begin icache_areq_o.fetch_valid = icache_areq_i.fetch_req; // we got an access error - if (iaccess_err) begin + if (iaccess_err[0]) 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 - }; + icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end 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 - }; + icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end end end else // --------- @@ -428,21 +451,38 @@ always_comb begin : instr_interface 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 - }; + icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end + + 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 - }; + icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; + icache_areq_o.fetch_exception.tval = ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end 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 - }; + if (!match_any_execute_region || (~(|enable_translation_i) && !pmp_instr_allow)) begin + icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; + icache_areq_o.fetch_exception.tval = riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]); + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end end end @@ -480,7 +520,7 @@ 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; +assign lsu_dtlb_hit_o = ([en_ld_st_translation_i[0]]) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; @@ -488,12 +528,12 @@ 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) ? // + (en_ld_st_translation_i[0] && !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]); assign lsu_dtlb_ppn_o [11:0] = - (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[0] && !misaligned_ex_q.valid) ? // dtlb_content[0].ppn[11:0] : // lsu_vaddr_n[23:12]; @@ -502,19 +542,19 @@ genvar i; 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)) ? // + ([en_ld_st_translation_i[0]] && !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)) ? // + (en_ld_st_translation_i[0] && !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] : // - (en_ld_st_translation_i && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + (en_ld_st_translation_i[0] && !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) ? + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (en_ld_st_translation_i[0] && !misaligned_ex_q.valid) ? dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] : lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; end @@ -523,7 +563,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[0]; @@ -539,10 +579,10 @@ always_comb begin : data_interface // 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 + daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i[0] && 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 + if (en_ld_st_translation_i[0] && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; // --------- @@ -560,6 +600,8 @@ always_comb begin : data_interface // 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}, From a0c2e11ee3c2cbaba07716b266084a0510f25ffe Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 20 Dec 2023 11:22:50 +0100 Subject: [PATCH 067/182] top mmu --- core/mmu_unify/cva6_mmu.sv | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6c9b7f3b3b..f908321ed6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -142,7 +142,7 @@ logic [riscv::VLEN-1:0] shared_tlb_vaddr; logic shared_tlb_hit; logic itlb_req; -logic [HYP_EXT*2:0] v_st_enbl_i +logic [HYP_EXT*2:0] v_st_enbl_i; // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; @@ -173,10 +173,10 @@ genvar x; end endgenerate - v_st_enbl_i[0]=v_i; + assign v_st_enbl_i[0]=v_i; if(HYP_EXT) begin - v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; + assign v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; end @@ -369,7 +369,7 @@ 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[0]]) ? // + (enable_translation_i[0]) ? // 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]); @@ -413,7 +413,7 @@ always_comb begin : instr_interface icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; icache_areq_o.fetch_exception.gva = v_i; end - + end icache_areq_o.fetch_valid = 1'b0; // --------- @@ -451,14 +451,14 @@ always_comb begin : instr_interface if (ptw_active && walking_instr) begin icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; if (ptw_error) - icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end else @@ -470,8 +470,8 @@ always_comb begin : instr_interface icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; icache_areq_o.fetch_exception.gva = v_i; end + end 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 @@ -520,7 +520,7 @@ 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[0]]) ? dtlb_lu_hit : 1'b1; +assign lsu_dtlb_hit_o = (en_ld_st_translation_i[0]) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; @@ -542,7 +542,7 @@ genvar i; 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[0]] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[0] && !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]; From dd0db5bea8eddd2390e761ca7d9e3a875e9461a6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 22 Dec 2023 16:05:56 +0100 Subject: [PATCH 068/182] Revert "top mmu" This reverts commit a0c2e11ee3c2cbaba07716b266084a0510f25ffe. --- core/mmu_unify/cva6_mmu.sv | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f908321ed6..6c9b7f3b3b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -142,7 +142,7 @@ logic [riscv::VLEN-1:0] shared_tlb_vaddr; logic shared_tlb_hit; logic itlb_req; -logic [HYP_EXT*2:0] v_st_enbl_i; +logic [HYP_EXT*2:0] v_st_enbl_i // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; @@ -173,10 +173,10 @@ genvar x; end endgenerate - assign v_st_enbl_i[0]=v_i; + v_st_enbl_i[0]=v_i; if(HYP_EXT) begin - assign v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; + v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; end @@ -369,7 +369,7 @@ 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[0]) ? // + ([enable_translation_i[0]]) ? // 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]); @@ -413,7 +413,7 @@ always_comb begin : instr_interface icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; icache_areq_o.fetch_exception.gva = v_i; end - end + icache_areq_o.fetch_valid = 1'b0; // --------- @@ -451,14 +451,14 @@ always_comb begin : instr_interface if (ptw_active && walking_instr) begin icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; if (ptw_error) - icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; + icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; + icache_areq_o.fetch_exception.valid = 1'b1; + if(HYP_EXT) begin + icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; + icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; + icache_areq_o.fetch_exception.gva = v_i; + end else @@ -470,8 +470,8 @@ always_comb begin : instr_interface icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; icache_areq_o.fetch_exception.gva = v_i; end - end 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 @@ -520,7 +520,7 @@ 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[0]) ? dtlb_lu_hit : 1'b1; +assign lsu_dtlb_hit_o = ([en_ld_st_translation_i[0]]) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; @@ -542,7 +542,7 @@ genvar i; 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[0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + ([en_ld_st_translation_i[0]] && !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]; From a14089d783c7dc74a825afb8b1dcccb8f170eeab Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 22 Dec 2023 16:07:10 +0100 Subject: [PATCH 069/182] Revert "common mmu top interface and instantiation in load store unit" This reverts commit 6890acb45670e3380af937ff1a8c83b4b126b43a. --- core/load_store_unit.sv | 9 +- core/mmu_unify/cva6_mmu.sv | 166 ++++++++++++++----------------------- 2 files changed, 63 insertions(+), 112 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index e630763537..95e622f757 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -199,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), @@ -218,13 +218,6 @@ module load_store_unit .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), - .lsu_tinst_i(0), - .csr_hs_ld_st_inst_o(), - .v_i(1), - .ld_st_v_i(0), - .hlvx_inst_i ( 0 ), - .hs_ld_st_inst_i ( 0 ), - .* ); end else begin : gen_no_mmu diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6c9b7f3b3b..1109f084df 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -32,43 +32,37 @@ import ariane_pkg::*; input logic clk_i, input logic rst_ni, input logic flush_i, - input logic [HYP_EXT:0] enable_translation_i, - input logic [HYP_EXT:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + 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 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 riscv::xlen_t lsu_tinst_i, // transformed instruction 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 - output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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) + 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 + 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 logic v_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - input logic [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic ld_st_v_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, + 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 [HYP_EXT*2:0], + 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 [HYP_EXT*2:0] flush_tlb_i, + input logic flush_tlb_i, // Performance counters output logic itlb_miss_o, output logic dtlb_miss_o, @@ -114,7 +108,7 @@ localparam type tlb_update_cva6_t1 = struct packed { } ; -logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page +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 @@ -142,7 +136,6 @@ logic [riscv::VLEN-1:0] shared_tlb_vaddr; logic shared_tlb_hit; logic itlb_req; -logic [HYP_EXT*2:0] v_st_enbl_i // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; @@ -173,11 +166,6 @@ genvar x; end endgenerate - v_st_enbl_i[0]=v_i; - - if(HYP_EXT) begin - v_st_enbl_i[2*HYP_EXT:HYP_EXT]=enable_translation_i; - end @@ -209,7 +197,7 @@ cva6_tlb #( .lu_is_page_o(itlb_is_page), .lu_hit_o (itlb_lu_hit), - .v_st_enbl_i(v_st_enbl_i) + .v_st_enbl_i(1) ); cva6_tlb #( @@ -233,13 +221,13 @@ 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), .lu_hit_o (dtlb_lu_hit), - .v_st_enbl_i(v_st_enbl_i) + .v_st_enbl_i(1) ); cva6_shared_tlb #( @@ -257,8 +245,8 @@ cva6_shared_tlb #( .rst_ni (rst_ni), .flush_i(flush_tlb_i), - .enable_translation_i (enable_translation_i[0]), - .en_ld_st_translation_i(en_ld_st_translation_i[0]), + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), .asid_i (asid_i[0]), // from TLBs @@ -269,7 +257,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_itlb1), @@ -326,8 +314,8 @@ cva6_ptw #( .itlb_req_i(itlb_req), // from CSR file - .satp_ppn_i(satp_ppn_i[0]), // ppn from satp - .mxr_i (mxr_i[0]), + .satp_ppn_i(satp_ppn_i), // ppn from satp + .mxr_i (mxr_i), // Performance counters .shared_tlb_miss_o(), //open for now @@ -369,7 +357,7 @@ 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[0]]) ? // + (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]); @@ -378,7 +366,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[0] && (|itlb_is_page[a:0]==0)) ? // + (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)))] : // icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; end @@ -394,25 +382,22 @@ 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[0] = icache_areq_i.fetch_req && enable_translation_i[0]&& (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].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)); - iaccess_err[1] = icache_areq_i.fetch_req && enable_translation_i[1] && !itlb_content[1].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 (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.cause = riscv::INSTR_ACCESS_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + 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; @@ -423,25 +408,17 @@ always_comb begin : instr_interface if (itlb_lu_hit) begin icache_areq_o.fetch_valid = icache_areq_i.fetch_req; // we got an access error - if (iaccess_err[0]) begin + if (iaccess_err) begin // throw a page fault - icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + 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.cause = riscv::INSTR_ACCESS_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end end else // --------- @@ -451,38 +428,21 @@ always_comb begin : instr_interface if (ptw_active && walking_instr) begin icache_areq_o.fetch_valid = ptw_error | ptw_access_exception; if (ptw_error) - icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; - icache_areq_o.fetch_exception.tval = {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end - - + 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.cause = riscv::INSTR_ACCESS_FAULT; - icache_areq_o.fetch_exception.tval = ptw_bad_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]; - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + 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.cause = riscv::INSTR_ACCESS_FAULT; - icache_areq_o.fetch_exception.tval = riscv::VLEN'(icache_areq_o.fetch_paddr[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0]); - icache_areq_o.fetch_exception.valid = 1'b1; - if(HYP_EXT) begin - icache_areq_o.fetch_exception.tval2 = {riscv::GPLEN{1'b0}}; - icache_areq_o.fetch_exception.tinst = {riscv::XLEN{1'b0}}; - icache_areq_o.fetch_exception.gva = v_i; - end + 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 @@ -520,7 +480,7 @@ 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[0]]) ? dtlb_lu_hit : 1'b1; +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; @@ -528,12 +488,12 @@ 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[0] && !misaligned_ex_q.valid) ? // + (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]); assign lsu_dtlb_ppn_o [11:0] = - (en_ld_st_translation_i[0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i && !misaligned_ex_q.valid) ? // dtlb_content[0].ppn[11:0] : // lsu_vaddr_n[23:12]; @@ -542,19 +502,19 @@ genvar i; 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[0]] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (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[0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (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] : // - (en_ld_st_translation_i[0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + (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[0] && !misaligned_ex_q.valid) ? + 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] : lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; end @@ -563,7 +523,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[0]; @@ -579,10 +539,10 @@ always_comb begin : data_interface // 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[0] && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode + 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[0] && !misaligned_ex_q.valid) begin + if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; // --------- @@ -600,8 +560,6 @@ always_comb begin : data_interface // 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}, From f05ebfba6feba25361d41d5b17e724fa6711a339 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 25 Jan 2024 17:11:39 +0100 Subject: [PATCH 070/182] common tlb --- core/mmu_unify/cva6_mmu.sv | 4 +- core/mmu_unify/cva6_tlb.sv | 661 +++++++++++++++++-------------------- 2 files changed, 307 insertions(+), 358 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1109f084df..2005cb5f3f 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -171,7 +171,7 @@ genvar x; cva6_tlb #( - .CVA6Cfg (CVA6Cfg), + // .CVA6Cfg (CVA6Cfg), .HYP_EXT(HYP_EXT), .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), @@ -201,7 +201,7 @@ cva6_tlb #( ); cva6_tlb #( - .CVA6Cfg (CVA6Cfg), + // .CVA6Cfg (CVA6Cfg), .HYP_EXT(HYP_EXT), .TLB_ENTRIES(DATA_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 5a6a17f2bc..31661e0c12 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Thales. +// Copyright (c) 2022 Bruno Sá and Zero-Day Labs. // 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 @@ -20,389 +20,350 @@ // =========================================================================== // // Revisions : // Date Version Author Description -// 2023-12-13 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support +// 2024-01-25 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support // =========================================================================== // - - module cva6_tlb - import ariane_pkg::*; -#( +module cva6_tlb import ariane_pkg::*; #( 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 HYP_EXT = 0, + 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 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 - // 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 , //[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] - output logic [PT_LEVELS-2:0] lu_is_page_o, - output logic lu_hit_o +)( + 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 +// 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, + 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] + 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 [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 [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; - 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 - logic [TLB_ENTRIES-1:0] match_stage; - logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; - //------------- - // 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,z,w; - 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 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 + plru_tree_n = plru_tree_q; + // 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 + automatic int unsigned idx_base, shift, new_index; + // 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 - 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 + // 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 + automatic logic en; + automatic int unsigned idx_base, shift, new_index; + 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 - 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 + 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 @@ -413,19 +374,13 @@ end 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 + else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end assert (ASID_WIDTH[0] >= 1) - else begin - $error("ASID width must be at least 1"); - $stop(); - end + 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); +function int countSetBits(logic[TLB_ENTRIES-1:0] vector); automatic int count = 0; foreach (vector[idx]) begin count += vector[idx]; @@ -433,16 +388,10 @@ function int countSetBits(logic [TLB_ENTRIES-1:0] vector); 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 +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 From 7a2730b4bfb18ed1cdff71d7f3d4f9d5703fd618 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 25 Jan 2024 17:36:11 +0100 Subject: [PATCH 071/182] cleanup tlb --- core/mmu_unify/cva6_tlb.sv | 558 ++++++++++++++++++------------------- 1 file changed, 279 insertions(+), 279 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 31661e0c12..b004985ddd 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Bruno Sá and Zero-Day Labs. +// 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 @@ -23,40 +23,40 @@ // 2024-01-25 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support // =========================================================================== // module cva6_tlb import ariane_pkg::*; #( - parameter type pte_cva6_t = logic, - parameter type tlb_update_cva6_t = logic, - parameter int unsigned TLB_ENTRIES = 4, - parameter int unsigned HYP_EXT = 0, - 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 VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int unsigned TLB_ENTRIES = 4, + parameter int unsigned HYP_EXT = 0, + 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 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 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 // Update TLB - input tlb_update_cva6_t update_i, +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, - 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] - output logic [PT_LEVELS-2:0] lu_is_page_o, - output logic lu_hit_o +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, +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] +output logic [PT_LEVELS-2:0] lu_is_page_o, +output logic lu_hit_o ); // SV39 defines three levels of page tables 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 valid; +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 valid; } [TLB_ENTRIES-1:0] tags_q, tags_n; pte_cva6_t [TLB_ENTRIES-1:0][HYP_EXT:0] content_q , content_n; @@ -79,104 +79,104 @@ pte_cva6_t g_content; genvar i,x,z,w; generate - for (i=0; i < TLB_ENTRIES; i++) begin - 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==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[HYP_EXT:0])): - ((&v_st_enbl_i[HYP_EXT:0]) ? // THIS WILL NEED TO BE OPTIMIZED ONCE WE MAKE IT WORK. INDEX 1 DOES NOT EXIST IN SV32. - ((tags_q[i].is_page[PT_LEVELS-1-x][0] && (tags_q[i].is_page[PT_LEVELS-2-x][1] || tags_q[i].is_page[PT_LEVELS-1-x][1])) // THE MIDDLE PART CORRESPONDS TO THE is_trans_2M FUNCTION - || (tags_q[i].is_page[PT_LEVELS-1-x][1] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): - tags_q[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[0] || tags_q[i].is_page[PT_LEVELS-1-x][1] && v_st_enbl_i[1])); - - //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && 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[12+VPN_LEN-1: 12+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 vpage_match for all TLB Entries and vaddr_level match (if there is a hit at each PT level for all TLB entries on the vaddr) - for(z=0;z 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 - automatic logic en; - automatic int unsigned idx_base, shift, new_index; - en = 1'b1; - for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin + plru_tree_n = plru_tree_q; + // 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 + automatic int unsigned idx_base, shift, new_index; + // 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; - - // 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 + // 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 + automatic logic en; + automatic int unsigned idx_base, shift, new_index; + 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 + 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 @@ -373,25 +373,25 @@ end `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[0] >= 1) - else begin $error("ASID width must be at least 1"); $stop(); end +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[0] >= 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; +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 +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 +else begin $error("More then one TLB entry selected for next replace!"); $stop(); end `endif //pragma translate_on From 78f12c56c0e498afeae9674d1444a7adbfb68efc Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 29 Jan 2024 14:11:34 +0100 Subject: [PATCH 072/182] common top - step 1 --- core/include/riscv_pkg.sv | 39 +- core/load_store_unit.sv | 89 +++- core/mmu_unify/cva6_mmu.sv | 1004 ++++++++++++++++++++++++------------ 3 files changed, 766 insertions(+), 366 deletions(-) diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 8b6ace69ae..729a7e6748 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -329,19 +329,24 @@ package riscv; // ---------------------- 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 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 or virtual user mode + localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from hypervisor-extended supervisor mode + localparam logic [XLEN-1:0] ENV_CALL_VSMODE = 10; // environment call from virtual 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] INSTR_GUEST_PAGE_FAULT= 20; // Instruction guest-page fault + localparam logic [XLEN-1:0] LOAD_GUEST_PAGE_FAULT = 21; // Load guest-page fault + localparam logic [XLEN-1:0] VIRTUAL_INSTRUCTION = 22; // virtual instruction + localparam logic [XLEN-1:0] STORE_GUEST_PAGE_FAULT= 23; // Store guest-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; @@ -364,6 +369,14 @@ package riscv; 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); + // ---------------------- + // PseudoInstructions Codes + // ---------------------- + localparam logic [XLEN-1:0] READ_32_PSEUDOINSTRUCTION = 32'h00002000; + localparam logic [XLEN-1:0] WRITE_32_PSEUDOINSTRUCTION = 32'h00002020; + localparam logic [XLEN-1:0] READ_64_PSEUDOINSTRUCTION = 64'h00003000; + localparam logic [XLEN-1:0] WRITE_64_PSEUDOINSTRUCTION = 64'h00003020; + // ----- // CSRs // ----- diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 95e622f757..707a93a199 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -175,50 +175,83 @@ module load_store_unit localparam ASID_LEN = (riscv::XLEN == 64) ? 16 : 9; 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}; + // 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]; + // 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; + // 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), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH ({ASID_WIDTH}), .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .flush_i(flush_i), + .enable_translation_i ({enable_translation_i}), + .en_ld_st_translation_i ({en_ld_st_translation_i}), + .icache_areq_i ( icache_areq_i ), + .icache_areq_o ( icache_areq_o ), // misaligned bypass - .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_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 + .misaligned_ex_i ( misaligned_exception ), + .lsu_req_i ( translation_req ), + .lsu_vaddr_i ( mmu_vaddr ), + .lsu_tinst_i ( 0 ), + .lsu_is_store_i ( st_translation_req ), + .csr_hs_ld_st_inst_o (0 ), + .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 + + .lsu_valid_o ( translation_valid ), + .lsu_paddr_o ( mmu_paddr ), + .lsu_exception_o ( mmu_exception ), + + .priv_lvl_i (priv_lvl_i ), + .ld_st_priv_lvl_i (ld_st_priv_lvl_i ), // connecting PTW to D$ IF - .req_port_i (dcache_req_ports_i[0]), - .req_port_o (dcache_req_ports_o[0]), + + + .sum_i ({sum_i}), + .mxr_i ({mxr_i}), + .hlvx_inst_i ( 0 ), + .hs_ld_st_inst_i ( 0 ), + // icache address translation requests - .icache_areq_i (icache_areq_i), + // .asid_to_be_flushed_i, + // .vmid_to_be_flushed_i, // .vaddr_to_be_flushed_i, - .icache_areq_o (icache_areq_o), + // .gpaddr_to_be_flushed_i, + + + // Hypervisor load/store signals + + + + .satp_ppn_i ({satp_ppn_i}), + .asid_i ({asid_i}), + .asid_to_be_flushed_i ({asid_to_be_flushed_i}), + .vaddr_to_be_flushed_i ({vaddr_to_be_flushed_i}), + .flush_tlb_i ({flush_tlb_i}), + + .itlb_miss_o (itlb_miss_o), + .dtlb_miss_o (dtlb_miss_o), + + .req_port_i ( dcache_req_ports_i [0] ), + .req_port_o ( dcache_req_ports_o [0] ), .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), - .* + .pmpaddr_i ); end else begin : gen_no_mmu diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 2005cb5f3f..9b5def2a36 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -24,55 +24,96 @@ import ariane_pkg::*; parameter int unsigned INSTR_TLB_ENTRIES = 4, parameter int unsigned DATA_TLB_ENTRIES = 4, parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0]= {1}, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], 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 + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + // input logic enable_g_translation_i, + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + // input logic en_ld_st_g_translation_i, // enable G-Stage translation for load/stores // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, + 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[HYP_EXT:0], // virtual address in - input logic lsu_is_store_i, // the translation is requested by a store + 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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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) + 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 + 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 riscv::priv_lvl_t priv_lvl_i, + // input logic v_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + // input logic ld_st_v_i, + input logic [HYP_EXT:0] sum_i, + // input logic vs_sum_i, + input logic [HYP_EXT:0] mxr_i, + // input logic vmxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_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 flush_tlb_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] + // input logic [riscv::PPNW-1:0] vsatp_ppn_i, + // input logic [riscv::PPNW-1:0] hgatp_ppn_i, + input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] + // input logic [ASID_WIDTH[0]-1:0] vs_asid_i, + input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i[HYP_EXT:0], + // input logic [VMID_WIDTH-1:0] vmid_i, + // input logic [VMID_WIDTH-1:0] vmid_to_be_flushed_i, + input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i[HYP_EXT:0], + // input logic [riscv::GPLEN-1:0] gpaddr_to_be_flushed_i, + input logic [HYP_EXT*2:0] flush_tlb_i, + // input logic flush_tlb_vvma_i, + // input logic flush_tlb_gvma_i, // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + 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, + 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 riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); + logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i [HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i [HYP_EXT:0]; + logic [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 [2:0] mmu_flush_i,mmu_v_st_enbl_i,mmu_v_st_enbl_d; + + assign mmu_flush_i = flush_tlb_i; + assign mmu_v_st_enbl_i = enable_translation_i; + assign mmu_v_st_enbl_d = en_ld_st_translation_i; + assign mmu_asid_to_be_flushed_i = asid_to_be_flushed_i; + assign mmu_vaddr_to_be_flushed_i = vaddr_to_be_flushed_i; + + genvar b; + generate + for (b=0; b < HYP_EXT+1; b++) begin + assign dtlb_mmu_asid_i[b] = b==0 ? + ((mmu_v_st_enbl_i[2*HYP_EXT] || mmu_flush_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + assign itlb_mmu_asid_i[b] = b==0 ? + (mmu_v_st_enbl_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + end + endgenerate // memory management, pte for cva6 localparam type pte_cva6_t = struct packed { @@ -108,26 +149,26 @@ localparam type tlb_update_cva6_t1 = struct packed { } ; -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 [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr -logic [riscv::VLEN-1:0] update_vaddr; + 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; tlb_update_cva6_t1 update_itlb1, update_dtlb1, update_shared_tlb1; logic itlb_lu_access; -pte_cva6_t [HYP_EXT:0] itlb_content ; +pte_cva6_t [HYP_EXT:0] mmu_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 [HYP_EXT:0] mmu_dtlb_content; logic [PT_LEVELS-2:0] dtlb_is_page; logic dtlb_lu_hit; @@ -137,6 +178,9 @@ logic shared_tlb_hit; logic itlb_req; +logic [riscv::GPLEN-1:0] itlb_gpaddr; +logic [riscv::GPLEN-1:0] dtlb_gpaddr; + // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; assign dtlb_lu_access = lsu_req_i; @@ -170,64 +214,56 @@ genvar x; -cva6_tlb #( - // .CVA6Cfg (CVA6Cfg), + cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), .HYP_EXT(HYP_EXT), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS), - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t) + .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_gpaddr_o(), - .lu_content_o (itlb_content), - - .lu_is_page_o(itlb_is_page), - .lu_hit_o (itlb_lu_hit), - .v_st_enbl_i(1) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( mmu_flush_i ), + .v_st_enbl_i ( mmu_v_st_enbl_i ), + .update_i ( update_itlb ), + .lu_access_i ( itlb_lu_access ), + .lu_asid_i ( itlb_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), + .lu_vaddr_i ( icache_areq_i.fetch_vaddr ), + .lu_content_o ( mmu_itlb_content ), + .lu_gpaddr_o ( itlb_gpaddr ), + .lu_is_page_o ( itlb_is_page ), + .lu_hit_o ( itlb_lu_hit ) ); cva6_tlb #( - // .CVA6Cfg (CVA6Cfg), - .HYP_EXT(HYP_EXT), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS), - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t) + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), + .HYP_EXT(HYP_EXT), + .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[0]), - .lu_gpaddr_o(), - .lu_content_o (dtlb_content), - - .lu_is_page_o(dtlb_is_page), - .lu_hit_o (dtlb_lu_hit), - .v_st_enbl_i(1) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( mmu_flush_i ), + .v_st_enbl_i ( mmu_v_st_enbl_d ), + .update_i ( update_dtlb ), + .lu_access_i ( dtlb_lu_access ), + .lu_asid_i ( dtlb_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), + .lu_vaddr_i ( lsu_vaddr_i ), + .lu_content_o ( mmu_dtlb_content ), + .lu_gpaddr_o ( dtlb_gpaddr ), + .lu_is_page_o ( dtlb_is_page ), + .lu_hit_o ( dtlb_lu_hit ) ); cva6_shared_tlb #( @@ -245,8 +281,8 @@ cva6_shared_tlb #( .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), + .enable_translation_i (enable_translation_i[0]), + .en_ld_st_translation_i(en_ld_st_translation_i[0]), .asid_i (asid_i[0]), // from TLBs @@ -314,8 +350,8 @@ cva6_ptw #( .itlb_req_i(itlb_req), // from CSR file - .satp_ppn_i(satp_ppn_i), // ppn from satp - .mxr_i (mxr_i), + .satp_ppn_i(satp_ppn_i[0]), // ppn from satp + .mxr_i (mxr_i[0]), // Performance counters .shared_tlb_miss_o(), //open for now @@ -348,108 +384,219 @@ cva6_ptw #( // .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[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : // + (|mmu_v_st_enbl_i[HYP_EXT:0]) ? // + (mmu_v_st_enbl_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : + mmu_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 - + 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)))] : // - icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (|mmu_v_st_enbl_i[HYP_EXT:0] && (|itlb_is_page[a:0]==0)) ? // + (mmu_v_st_enbl_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].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)))]: + mmu_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 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[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. - // 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 + // 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[0] = icache_areq_i.fetch_req && mmu_v_st_enbl_i[0] && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && mmu_itlb_content[0].u)); + + if(HYP_EXT==1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && mmu_v_st_enbl_i[HYP_EXT] && !mmu_itlb_content[HYP_EXT].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 ((|mmu_v_st_enbl_i[HYP_EXT:0])) 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 + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + end + else 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; - - // --------- - // 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 + + icache_areq_o.fetch_valid = 1'b0; + + // // 4K page + // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? mmu_itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // // Mega page + // if (itlb_is_2M) begin + // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; + // end + // // Giga page + // if (itlb_is_1G) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end + + end else if (!pmp_instr_allow) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + mmu_v_st_enbl_i[2*HYP_EXT], + 1'b1 + }; + end else begin + if (HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[2*HYP_EXT], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end + end + end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end + 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[0]) || (!(|mmu_v_st_enbl_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 - 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} -); +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 #( @@ -471,167 +618,359 @@ pmp #( //----------------------- // Data Interface //----------------------- -logic [riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_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; -logic dtlb_hit_n, dtlb_hit_q; +logic [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +// logic [riscv::VLEN-1:0] lsu_gpaddr_n, lsu_gpaddr_q; +logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; +logic hs_ld_st_inst_n, hs_ld_st_inst_q; +pte_cva6_t [HYP_EXT:0] dtlb_pte_n, dtlb_pte_q; +// riscv::pte_t dtlb_gpte_n, dtlb_gpte_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; +assign lsu_dtlb_hit_o = (|mmu_v_st_enbl_d[HYP_EXT:0]) ? dtlb_lu_hit : 1'b1; // Wires to PMP checks riscv::pmp_access_t pmp_access_type; -logic pmp_data_allow; +logic pmp_data_allow; -assign lsu_paddr_o [11:0] = lsu_vaddr_q[11:0]; +assign lsu_paddr_o [11:0] = lsu_vaddr_q[0][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]); + (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (mmu_v_st_enbl_d[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((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] : // - lsu_vaddr_n[23:12]; - + (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[11:0]: + mmu_dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][23:12]; + genvar i; - generate +generate - for (i=0; i < PT_LEVELS-1; i++) begin + 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]; + (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (mmu_v_st_enbl_d[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[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[0].ppn[riscv::PPNW-1:PPNWMin+1] : - lsu_vaddr_n[riscv::PLEN-1:PPNWMin+1] ; - end + (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]: + mmu_dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] ): // + (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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] = (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + mmu_dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; + end + +endgenerate - 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[0]; - lsu_req_n = lsu_req_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = dtlb_content[0]; - 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 - }; - // 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 + // save request and DTLB response + lsu_vaddr_n[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_i; + + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = mmu_dtlb_content; + // dtlb_gpte_n = dtlb_g_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_2M; + // dtlb_is_1G_n = dtlb_is_1G; + + if(HYP_EXT==1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + 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 - }; + // lsu_paddr_o = lsu_vaddr_q[0][riscv::PLEN-1:0]; + // lsu_dtlb_ppn_o = lsu_vaddr_n[0][riscv::PLEN-1:12]; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = mmu_v_st_enbl_d[0] && + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (mmu_v_st_enbl_d[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); + + if(HYP_EXT==1) + daccess_err[HYP_EXT] = mmu_v_st_enbl_d[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|mmu_v_st_enbl_d[HYP_EXT:0]) && !misaligned_ex_q.valid) begin + lsu_valid_o = 1'b0; + // // 4K page + // lsu_paddr_o = {(mmu_v_st_enbl_d[1]) ? dtlb_pte_q[1].ppn : dtlb_pte_q[0].ppn, lsu_vaddr_q[0][11:0]}; + // lsu_dtlb_ppn_o = (mmu_v_st_enbl_d[1]) ? dtlb_g_content.ppn : dtlb_content.ppn; + // // Mega page + // if (dtlb_is_page_q[1]) begin + // lsu_paddr_o[20:12] = lsu_vaddr_q[0][20:12]; + // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[0][20:12]; + // end + // // Giga page + // if (dtlb_is_page_q[0]) begin + // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[0][PPNWMin:12]; + // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[0][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(HYP_EXT==1 && mmu_v_st_enbl_d[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end else if (mmu_v_st_enbl_d[0] && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN-riscv::PLEN{1'b0}},lsu_paddr_o}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else 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 + + // this is a load + end else begin + if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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 + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end else begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + end else begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end else begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + 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 + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end + end 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 + 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::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 - }; + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 1'b1 + }; + end + else 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 + end else begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + mmu_v_st_enbl_d[HYP_EXT*2], + 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 (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 - 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), @@ -651,23 +990,38 @@ pmp #( // ---------- // Registers // ---------- +// ---------- + // 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; + lsu_vaddr_q <= '0; + // lsu_gpaddr_q <= '0; + lsu_tinst_q <= '0; + hs_ld_st_inst_q <= '0; + lsu_req_q <= '0; + misaligned_ex_q <= '0; + dtlb_pte_q <= '0; + // dtlb_gpte_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; - 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_vaddr_q <= lsu_vaddr_n; + // lsu_gpaddr_q <= lsu_gpaddr_n; + lsu_tinst_q <= lsu_tinst_n; + hs_ld_st_inst_q <= hs_ld_st_inst_n; + lsu_req_q <= lsu_req_n; + misaligned_ex_q <= misaligned_ex_n; + dtlb_pte_q <= dtlb_pte_n; + // dtlb_gpte_q <= dtlb_gpte_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 From 7547e8c669244b787676789f0b2bf64e47d4aad8 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 29 Jan 2024 15:43:21 +0100 Subject: [PATCH 073/182] fix lsu_vaddr connection, sv39 boots --- core/include/ariane_pkg.sv | 30 ++++++++++++++++++++++++++++++ core/mmu_unify/cva6_mmu.sv | 8 ++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index 446f609a61..81234d311e 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -1004,4 +1004,34 @@ package ariane_pkg; default: return 2'b11; endcase endfunction + + // computes the paddr based on the page size, ppn and offset + function automatic logic [(riscv::GPLEN-1):0] make_gpaddr(input logic s_st_enbl, input logic is_1G, input logic is_2M, input logic [(riscv::VLEN-1):0] vaddr, input riscv::pte_t pte); + logic [(riscv::GPLEN-1):0] gpaddr; + if (s_st_enbl) begin + gpaddr = {pte.ppn[(riscv::GPPNW-1):0], vaddr[11:0]}; + // Giga page + if (is_1G) gpaddr[29:12] = vaddr[29:12]; + // Mega page + if (is_2M) gpaddr[20:12] = vaddr[20:12]; + end else begin + gpaddr = vaddr[(riscv::GPLEN-1):0]; + end + return gpaddr; +endfunction : make_gpaddr + +// computes the final gppn based on the guest physical address +function automatic logic [(riscv::GPPNW-1):0] make_gppn(input logic s_st_enbl, input logic is_1G, input logic is_2M, input logic [28:0] vpn, input riscv::pte_t pte); + logic [(riscv::GPPNW-1):0] gppn; + if (s_st_enbl) begin + gppn = pte.ppn[(riscv::GPPNW-1):0]; + if(is_2M) + gppn[8:0] = vpn[8:0]; + if(is_1G) + gppn[17:0] = vpn[17:0]; + end else begin + gppn = vpn; + end + return gppn; +endfunction : make_gppn endpackage diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 9b5def2a36..adb74a7052 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -293,7 +293,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_itlb1), @@ -506,7 +506,7 @@ always_comb begin : instr_interface if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, mmu_v_st_enbl_i[HYP_EXT*2], @@ -580,7 +580,7 @@ always_comb begin : instr_interface if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, mmu_v_st_enbl_i[HYP_EXT*2], @@ -780,7 +780,7 @@ always_comb begin : data_interface if(HYP_EXT==1) begin lsu_exception_o = { riscv::ST_ACCESS_FAULT, - {{riscv::XLEN-riscv::PLEN{1'b0}},lsu_paddr_o}, + {riscv::XLEN '(lsu_paddr_o)}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, mmu_v_st_enbl_d[HYP_EXT*2], From 4821c4fb6e64d61c884e20e91fa346a1aeafeb53 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 29 Jan 2024 16:21:16 +0100 Subject: [PATCH 074/182] fix remaining indexes in tlb --- core/mmu_unify/cva6_tlb.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index b004985ddd..945adffdc2 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -85,9 +85,9 @@ for (i=0; i < TLB_ENTRIES; i++) begin assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[HYP_EXT:0])): ((&v_st_enbl_i[HYP_EXT:0]) ? - ((tags_q[i].is_page[PT_LEVELS-1-x][0] && (tags_q[i].is_page[PT_LEVELS-2-x][1] || tags_q[i].is_page[PT_LEVELS-1-x][1])) - || (tags_q[i].is_page[PT_LEVELS-1-x][1] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): - tags_q[i].is_page[PT_LEVELS-1-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][0] && (tags_q[i].is_page[PT_LEVELS-2-x][HYP_EXT] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT])) + || (tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): + tags_q[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[0] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[HYP_EXT])); //identify if vpn matches at all PT levels for all TLB entries assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[0]) ? // From aabce85c558a6e90cba75ede470c77ba00b8168f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 29 Jan 2024 16:23:38 +0100 Subject: [PATCH 075/182] unconnect unused output in mmu top --- core/load_store_unit.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 707a93a199..a1ead8a5e8 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -209,7 +209,7 @@ module load_store_unit .lsu_vaddr_i ( mmu_vaddr ), .lsu_tinst_i ( 0 ), .lsu_is_store_i ( st_translation_req ), - .csr_hs_ld_st_inst_o (0 ), + .csr_hs_ld_st_inst_o ( ), .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 From e9918ad4810c6cf45c1f9c8a8f57f304c6623da3 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 29 Jan 2024 19:32:10 +0100 Subject: [PATCH 076/182] FIX TLB ERROR --- core/mmu_unify/cva6_tlb.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 945adffdc2..8ec9463eea 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -213,7 +213,7 @@ always_comb begin : update_flush if (flush_i[0]) begin - if(!tags_q[i].v_st_enbl[HYP_EXT*2]) begin + if(!tags_q[i].v_st_enbl[HYP_EXT*2] || HYP_EXT==0) begin // invalidate logic // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0] ) From 458b5c4dec8b62f991b092084bfa689d4a641a5b Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 2 Feb 2024 13:49:03 +0100 Subject: [PATCH 077/182] common ptw no shared tlb --- core/mmu_unify/cva6_mmu.sv | 1592 ++++++++++++++++-------------------- core/mmu_unify/cva6_ptw.sv | 991 +++++++++++++--------- 2 files changed, 1317 insertions(+), 1266 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index adb74a7052..f369b9d8d5 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -9,7 +9,8 @@ // specific language governing permissions and limitations under the License. // // Author: Angela Gonzalez, PlanV Technology -// Date: 07/12/2023 +// Date: 26/01/2024 +// // 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. @@ -17,1011 +18,860 @@ // by Florian Zaruba and the MMU Sv32 developed by Sebastien Jacq. -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 logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - parameter int unsigned ASID_LEN = 1, - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 +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 logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - // input logic enable_g_translation_i, - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - // input logic en_ld_st_g_translation_i, // enable G-Stage 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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction - // 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 logic v_i, - input riscv::priv_lvl_t ld_st_priv_lvl_i, - // input logic ld_st_v_i, - input logic [HYP_EXT:0] sum_i, - // input logic vs_sum_i, - input logic [HYP_EXT:0] mxr_i, - // input logic vmxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] - // input logic [riscv::PPNW-1:0] vsatp_ppn_i, - // input logic [riscv::PPNW-1:0] hgatp_ppn_i, - input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] - // input logic [ASID_WIDTH[0]-1:0] vs_asid_i, - input logic [ASID_WIDTH[0]-1:0] asid_to_be_flushed_i[HYP_EXT:0], - // input logic [VMID_WIDTH-1:0] vmid_i, - // input logic [VMID_WIDTH-1:0] vmid_to_be_flushed_i, - input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i[HYP_EXT:0], - // input logic [riscv::GPLEN-1:0] gpaddr_to_be_flushed_i, - input logic [HYP_EXT*2:0] flush_tlb_i, - // input logic flush_tlb_vvma_i, - // input logic flush_tlb_gvma_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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] 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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction + // 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 [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] + + input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i [HYP_EXT:0]; logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i [HYP_EXT:0]; - logic [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 [2:0] mmu_flush_i,mmu_v_st_enbl_i,mmu_v_st_enbl_d; - - assign mmu_flush_i = flush_tlb_i; - assign mmu_v_st_enbl_i = enable_translation_i; - assign mmu_v_st_enbl_d = en_ld_st_translation_i; - assign mmu_asid_to_be_flushed_i = asid_to_be_flushed_i; - assign mmu_vaddr_to_be_flushed_i = vaddr_to_be_flushed_i; genvar b; generate for (b=0; b < HYP_EXT+1; b++) begin assign dtlb_mmu_asid_i[b] = b==0 ? - ((mmu_v_st_enbl_i[2*HYP_EXT] || mmu_flush_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; assign itlb_mmu_asid_i[b] = b==0 ? - (mmu_v_st_enbl_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; end endgenerate // 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][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; -} ; - -localparam type tlb_update_cva6_t1 = 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 [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page - logic [HYP_EXT:0] 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 [HYP_EXT:0] ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr + 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][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; + } ; + + logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest 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; -tlb_update_cva6_t1 update_itlb1, update_dtlb1, update_shared_tlb1; - -logic itlb_lu_access; -pte_cva6_t [HYP_EXT:0] mmu_itlb_content ; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; - -logic dtlb_lu_access; -pte_cva6_t [HYP_EXT:0] mmu_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; - -logic [riscv::GPLEN-1:0] itlb_gpaddr; -logic [riscv::GPLEN-1:0] dtlb_gpaddr; - -// Assignments -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - -assign update_shared_tlb.valid = update_shared_tlb1.valid; -assign update_shared_tlb.vpn = update_shared_tlb1.vpn; -assign update_shared_tlb.asid[0] = update_shared_tlb1.asid; -assign update_shared_tlb.content[0] = update_shared_tlb1.content; - -assign update_itlb.valid = update_itlb1.valid; -assign update_itlb.vpn = update_itlb1.vpn; -assign update_itlb.asid[0] = update_itlb1.asid; -assign update_itlb.content[0] = update_itlb1.content; - -assign update_dtlb.valid = update_dtlb1.valid; -assign update_dtlb.vpn = update_dtlb1.vpn; -assign update_dtlb.asid[0] = update_dtlb1.asid; -assign update_dtlb.content[0] = update_dtlb1.content; - -genvar x; - generate - for (x=0; x < PT_LEVELS-1; x++) begin - assign update_shared_tlb.is_page[x][0] = update_shared_tlb1.is_page[x]; - assign update_itlb.is_page[x][0] = update_itlb1.is_page[x]; - assign update_dtlb.is_page[x][0] = update_dtlb1.is_page[x]; - end - endgenerate - - - - - - cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), - .HYP_EXT(HYP_EXT), - .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 ( mmu_flush_i ), - .v_st_enbl_i ( mmu_v_st_enbl_i ), - .update_i ( update_itlb ), - .lu_access_i ( itlb_lu_access ), - .lu_asid_i ( itlb_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), - .lu_vaddr_i ( icache_areq_i.fetch_vaddr ), - .lu_content_o ( mmu_itlb_content ), - .lu_gpaddr_o ( itlb_gpaddr ), - .lu_is_page_o ( itlb_is_page ), - .lu_hit_o ( itlb_lu_hit ) -); - -cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), - .HYP_EXT(HYP_EXT), - .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 ( mmu_flush_i ), - .v_st_enbl_i ( mmu_v_st_enbl_d ), - .update_i ( update_dtlb ), - .lu_access_i ( dtlb_lu_access ), - .lu_asid_i ( dtlb_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), - .lu_vaddr_i ( lsu_vaddr_i ), - .lu_content_o ( mmu_dtlb_content ), - .lu_gpaddr_o ( dtlb_gpaddr ), - .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[0]), - .ASID_LEN (ASID_LEN), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS), - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t1) -) i_shared_tlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i(flush_tlb_i), - - .enable_translation_i (enable_translation_i[0]), - .en_ld_st_translation_i(en_ld_st_translation_i[0]), - - .asid_i (asid_i[0]), - // 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_itlb1), - .dtlb_update_o(update_dtlb1), - - // 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[0]), - .VPN_LEN(VPN_LEN), - .PT_LEVELS(PT_LEVELS), - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t1) -) 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_tlb1), - - .update_vaddr_o(update_vaddr), - - .asid_i(asid_i[0]), - - // 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[0]), // ppn from satp - .mxr_i (mxr_i[0]), - - // 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_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 -// .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 -// ); + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + pte_cva6_t [HYP_EXT:0] mmu_itlb_content ; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + logic [riscv::GPLEN-1:0] itlb_gpaddr; + logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + + logic dtlb_lu_access; + pte_cva6_t [HYP_EXT:0] mmu_dtlb_content ; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; + logic dtlb_lu_hit; + logic [riscv::GPLEN-1:0] dtlb_gpaddr; + + // Assignments + + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( enable_translation_i ), + .update_i ( update_itlb ), + .lu_access_i ( itlb_lu_access ), + .lu_asid_i ( itlb_mmu_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 ( mmu_itlb_content ), + .lu_gpaddr_o ( itlb_gpaddr ), + .lu_is_page_o ( itlb_is_page ), + .lu_hit_o ( itlb_lu_hit ) + ); + + cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( en_ld_st_translation_i ), + .update_i ( update_dtlb ), + .lu_access_i ( dtlb_lu_access ), + .lu_asid_i ( dtlb_mmu_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 ( mmu_dtlb_content ), + .lu_gpaddr_o ( dtlb_gpaddr ), + .lu_is_page_o ( dtlb_is_page ), + .lu_hit_o ( dtlb_lu_hit ) + ); + + + cva6_ptw #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT(HYP_EXT), + .ASID_WIDTH ( ASID_WIDTH ), + .VPN_LEN(VPN_LEN), + .CVA6Cfg (CVA6Cfg), + .PT_LEVELS(PT_LEVELS) + ) 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 ), + .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o ( update_vaddr ), + .itlb_update_o ( update_itlb ), + .dtlb_update_o ( update_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 ), + .hlvx_inst_i ( hlvx_inst_i ), + + .req_port_i ( req_port_i ), + .req_port_o ( req_port_o ), + .mxr_i (mxr_i ), + + .satp_ppn_i (satp_ppn_i ), + + .pmpcfg_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_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 + // .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] = // - (|mmu_v_st_enbl_i[HYP_EXT:0]) ? // - (mmu_v_st_enbl_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : - mmu_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 - - 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] = // - (|mmu_v_st_enbl_i[HYP_EXT:0] && (|itlb_is_page[a:0]==0)) ? // - (mmu_v_st_enbl_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].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)))]: - mmu_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 - -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[0] = icache_areq_i.fetch_req && mmu_v_st_enbl_i[0] && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && mmu_itlb_content[0].u)); - - if(HYP_EXT==1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && mmu_v_st_enbl_i[HYP_EXT] && !mmu_itlb_content[HYP_EXT].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 ((|mmu_v_st_enbl_i[HYP_EXT:0])) 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 - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - mmu_v_st_enbl_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - - + 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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : + mmu_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 + + 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[HYP_EXT:0] && (|itlb_is_page[a:0]==0)) ? // + (enable_translation_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].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)))]: + mmu_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 - icache_areq_o.fetch_valid = 1'b0; + endgenerate - // // 4K page - // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? mmu_itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_2M) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_1G) 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[HYP_EXT*2], - 1'b1 - }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault + // 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[0] = icache_areq_i.fetch_req && enable_translation_i[0] && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && mmu_itlb_content[0].u)); + + if(HYP_EXT==1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !mmu_itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 if(HYP_EXT==1) 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}, {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[HYP_EXT*2], + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[HYP_EXT*2], + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end + + + 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + + icache_areq_o.fetch_valid = 1'b0; + + // // 4K page + // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? mmu_itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // // Mega page + // if (itlb_is_2M) begin + // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; + // end + // // Giga page + // if (itlb_is_1G) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - mmu_v_st_enbl_i[2*HYP_EXT], + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; - end else begin - if (HYP_EXT==1) begin + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[2*HYP_EXT], + enable_translation_i[HYP_EXT*2], 1'b1 }; end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - end - end - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end - end - 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[0]) || (!(|mmu_v_st_enbl_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_i[HYP_EXT*2], - 1'b1 - }; - end - else 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 -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 [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -// logic [riscv::VLEN-1:0] lsu_gpaddr_n, lsu_gpaddr_q; -logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; -logic hs_ld_st_inst_n, hs_ld_st_inst_q; -pte_cva6_t [HYP_EXT:0] dtlb_pte_n, dtlb_pte_q; -// riscv::pte_t dtlb_gpte_n, dtlb_gpte_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 = (|mmu_v_st_enbl_d[HYP_EXT:0]) ? 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[0][11:0]; -assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (mmu_v_st_enbl_d[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -assign lsu_dtlb_ppn_o [11:0] = - (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[11:0]: - mmu_dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][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] = // - (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (mmu_v_st_enbl_d[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: - dtlb_pte_q[0].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[0][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] = // - (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]: - mmu_dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] ): // - (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? - lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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] = (|mmu_v_st_enbl_d[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (mmu_v_st_enbl_d[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - mmu_dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): - lsu_vaddr_n[0][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[0] = lsu_vaddr_i; - lsu_tinst_n = lsu_tinst_i; - - lsu_req_n = lsu_req_i; - hs_ld_st_inst_n = hs_ld_st_inst_i; - misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = mmu_dtlb_content; - // dtlb_gpte_n = dtlb_g_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_2M; - // dtlb_is_1G_n = dtlb_is_1G; - - if(HYP_EXT==1) begin - lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; - end - - // lsu_paddr_o = lsu_vaddr_q[0][riscv::PLEN-1:0]; - // lsu_dtlb_ppn_o = lsu_vaddr_n[0][riscv::PLEN-1:12]; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = mmu_v_st_enbl_d[0] && - ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (mmu_v_st_enbl_d[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); - - if(HYP_EXT==1) - daccess_err[HYP_EXT] = mmu_v_st_enbl_d[HYP_EXT] && !dtlb_pte_q[1].u; - // translation is enabled and no misaligned exception occurred - if ((|mmu_v_st_enbl_d[HYP_EXT:0]) && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; - // // 4K page - // lsu_paddr_o = {(mmu_v_st_enbl_d[1]) ? dtlb_pte_q[1].ppn : dtlb_pte_q[0].ppn, lsu_vaddr_q[0][11:0]}; - // lsu_dtlb_ppn_o = (mmu_v_st_enbl_d[1]) ? dtlb_g_content.ppn : dtlb_content.ppn; - // // Mega page - // if (dtlb_is_page_q[1]) begin - // lsu_paddr_o[20:12] = lsu_vaddr_q[0][20:12]; - // lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[0][20:12]; - // end - // // Giga page - // if (dtlb_is_page_q[0]) begin - // lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[0][PPNWMin:12]; - // lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[0][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(HYP_EXT==1 && mmu_v_st_enbl_d[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_d[HYP_EXT*2], - 1'b1 - }; - end else if (mmu_v_st_enbl_d[0] && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 - }; + }; end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin + + end else if (!pmp_instr_allow) begin if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {riscv::XLEN '(lsu_paddr_o)}, + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; end else 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 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; end end - - // this is a load - end else begin - if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - mmu_v_st_enbl_d[HYP_EXT*2], - 1'b1 - }; - // check for sufficient access privileges - throw a page fault if necessary - end else if (daccess_err[0]) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], 1'b1 }; + end else begin + if (HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin + end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 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 + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; end end end - end else + 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[0]) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 + end - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error[0]) begin - // an error makes the translation valid + // 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 [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; + logic hs_ld_st_inst_n, hs_ld_st_inst_q; + pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + assign lsu_dtlb_ppn_o [11:0] = + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[11:0]: + mmu_dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]: + mmu_dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] ): // + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + mmu_dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][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[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_i; + + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_i; + misaligned_ex_n = misaligned_ex_i; + dtlb_pte_n = mmu_dtlb_content; + dtlb_hit_n = dtlb_lu_hit; + lsu_is_store_n = lsu_is_store_i; + dtlb_is_page_n = dtlb_is_page; + + if(HYP_EXT==1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + end + + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = en_ld_st_translation_i[0] && + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); + + if(HYP_EXT==1) + daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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 - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - mmu_v_st_enbl_d[HYP_EXT*2], + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else begin + end else if (en_ld_st_translation_i[0] && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; end else begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, 1'b1 }; end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {riscv::XLEN '(lsu_paddr_o)}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 + + // this is a load end else begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin lsu_exception_o = { riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - mmu_v_st_enbl_d[HYP_EXT*2], + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else begin + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; end else begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, 1'b1 }; end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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 + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + end else begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end 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 + 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 + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][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 if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 + end else begin + if(HYP_EXT==1) begin lsu_exception_o = { riscv::LD_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; end else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + 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 - // 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 - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], - 1'b1 - }; - end - else 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 - end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - mmu_v_st_enbl_d[HYP_EXT*2], - 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 ) + ); -// 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 -// ---------- -// ---------- + // ---------- // Registers // ---------- -always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - lsu_vaddr_q <= '0; - // lsu_gpaddr_q <= '0; - lsu_tinst_q <= '0; - hs_ld_st_inst_q <= '0; - lsu_req_q <= '0; - misaligned_ex_q <= '0; - dtlb_pte_q <= '0; - // dtlb_gpte_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_gpaddr_q <= lsu_gpaddr_n; - lsu_tinst_q <= lsu_tinst_n; - hs_ld_st_inst_q <= hs_ld_st_inst_n; - lsu_req_q <= lsu_req_n; - misaligned_ex_q <= misaligned_ex_n; - dtlb_pte_q <= dtlb_pte_n; - // dtlb_gpte_q <= dtlb_gpte_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 + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + lsu_vaddr_q <= '0; + lsu_tinst_q <= '0; + hs_ld_st_inst_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_tinst_q <= lsu_tinst_n; + hs_ld_st_inst_q <= hs_ld_st_inst_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 diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index e43fe442f5..bbf43eaffe 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -8,419 +8,620 @@ // 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 a 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 -// =========================================================================== // +// Author: Angela Gonzalez, PlanV Technology +// Date: 02/02/2024 +// Description: Hardware-PTW (Page-Table-Walker) for CVA6 supporting sv32, sv39 and sv39x4. +// This module is an merge of the PTW Sv39 developed by Florian Zaruba, +// the PTW Sv32 developed by Sebastien Jacq and the PTW Sv39x4 by Bruno Sá. /* verilator lint_off WIDTH */ -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, - 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 +module cva6_ptw import ariane_pkg::*; #( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, + parameter int unsigned VPN_LEN = 1, + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + 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 [HYP_EXT*2:0] ptw_error_o, // set when an error occurred + output logic ptw_access_exception_o, // set when an PMP access exception occured + + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + + input logic hlvx_inst_i, // is a HLVX load/store instruction + + 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 TLBs, update logic + output tlb_update_cva6_t itlb_update_o, + output tlb_update_cva6_t dtlb_update_o, + + output logic [riscv::VLEN-1:0] update_vaddr_o, + + input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] + + // 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, + // from CSR file + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] + input logic [HYP_EXT:0] mxr_i, + + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, + // PMP + + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, + output logic [HYP_EXT:0][riscv::PLEN-1:0] bad_paddr_o ); -// input registers -logic data_rvalid_q; -riscv::xlen_t data_rdata_q; - -pte_cva6_t pte; -assign pte = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); - - -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] 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; -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)); - - // 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)) && (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) -); + // input registers + logic data_rvalid_q; + riscv::xlen_t data_rdata_q; + + logic [PT_LEVELS-1:0] misaligned_page; + pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] + // register to perform context switch between stages + // pte_cva6_t gpte_q, gpte_d; + assign pte[0] = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); + + enum logic[2:0] { + IDLE, + WAIT_GRANT, + PTE_LOOKUP, + WAIT_RVALID, + PROPAGATE_ERROR, + PROPAGATE_ACCESS_ERROR + } state_q, state_d; + + logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_q, ptw_lvl_n; + + // define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE + // S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs + // G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp) + // G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp) + enum logic [1:0] { + S_STAGE, + G_INTERMED_STAGE, + G_FINAL_STAGE + } ptw_stage_q, ptw_stage_d; + + // 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 ASIDs + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; + + // register the VPN we need to walk, SV39 defines a 39 bit virtual address + logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; + logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; + // register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage + logic [riscv::GPLEN-1:0] gpaddr_q, gpaddr_n,gpaddr_base; + logic [PT_LEVELS-2:0][riscv::GPLEN-1:0] gpaddr; + // 4 byte aligned physical pointer + logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; + logic [riscv::PLEN-1:0] gptw_pptr_q, gptw_pptr_n; + + // Assignments + assign update_vaddr_o = vaddr_q; + + assign ptw_active_o = (state_q != IDLE); + 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; + + // ----------- + // TLB Update + // ----------- + + assign gpaddr_base = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; + + genvar z,w; + generate + for (z=0; z < PT_LEVELS-1; z++) begin + + // 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[z] = (ptw_lvl_q[0] == (z)) && (pte[0].ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-z)-1:0] != '0); + + //record the vaddr corresponding to each level + for (w=0; w < HYP_EXT*2+1; w++) begin + assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))] : + w==1 ? gptw_pptr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]: + gpaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]; + end + assign gpaddr[z][VPN_LEN-(VPN_LEN/PT_LEVELS):0]= (ptw_lvl_q[0] == z) ? vaddr_q[VPN_LEN-(VPN_LEN/PT_LEVELS):0] : gpaddr_base[VPN_LEN-(VPN_LEN/PT_LEVELS):0]; + assign gpaddr[z][VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1] : gpaddr_base[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]; + assign gpaddr[z][riscv::GPLEN-1:VPN_LEN+1]= gpaddr_base[riscv::GPLEN-1:VPN_LEN+1]; - 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 -//------------------- -// 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'(PT_LEVELS); - 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 = 0; - 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 + endgenerate + + always_comb begin : tlb_update + + itlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + dtlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + + // update the correct page table level + for (int unsigned y=0; y < HYP_EXT+1; y++) begin + for (int unsigned x=0; x < PT_LEVELS-1; x++) begin + if(&enable_translation_i[HYP_EXT:0] && HYP_EXT==1) + itlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); + else if(enable_translation_i[0]) + itlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; + else + itlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; + + if(&en_ld_st_translation_i[HYP_EXT:0]) + dtlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); + else if(en_ld_st_translation_i[0]) + dtlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; + else + dtlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; + end + // set the global mapping bit + if(enable_translation_i[HYP_EXT] && HYP_EXT==1) begin + itlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; + end else begin + itlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; + end + if(en_ld_st_translation_i[HYP_EXT] && HYP_EXT==1) begin + dtlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; 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 + dtlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; + end + end + // output the correct ASIDs + itlb_update_o.asid = tlb_update_asid_q; + dtlb_update_o.asid = tlb_update_asid_q; + + bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; + if(HYP_EXT==1) + bad_paddr_o[HYP_EXT][riscv::GPLEN:0] = ptw_error_o[HYP_EXT] ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[riscv::GPLEN:0] : gpaddr_q) : 'b0; + end + + assign req_port_o.tag_valid = tag_valid_q; + + logic allow_access; + + + + pmp #( + .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 ) + ); + + //------------------- + // 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, or if any bits or encodings + // that are reserved for future standard use are set within pte, stop and raise + // a page-fault exception corresponding to the original access type. + // 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 + automatic logic [riscv::PLEN-1:0] pptr; + // automatic logic [riscv::GPLEN-1:0] gpaddr; + // default assignments + // PTW memory interface + tag_valid_n = 1'b0; + req_port_o.data_req = 1'b0; + req_port_o.data_be = 8'hFF; + req_port_o.data_size = 2'b11; + req_port_o.data_we = 1'b0; + ptw_error_o = '0; + ptw_access_exception_o = 1'b0; + itlb_update_o.valid = 1'b0; + dtlb_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; + gptw_pptr_n = gptw_pptr_q; + state_d = state_q; + ptw_stage_d = ptw_stage_q; + global_mapping_n = global_mapping_q; + // input registers + tlb_update_asid_n = tlb_update_asid_q; + vaddr_n = vaddr_q; + gpaddr_n = gpaddr_q; + pptr = ptw_pptr_q; + // gpaddr = gpaddr_q; + + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; + + if(HYP_EXT==1) + pte[HYP_EXT*2] = pte[HYP_EXT]; + + case (state_q) + + IDLE: begin + // by default we start with the top-most page table + ptw_lvl_n = '0; + global_mapping_n = 1'b0; + is_instr_ptw_n = 1'b0; + gpaddr_n = '0; + + if(HYP_EXT==1) + pte[HYP_EXT*2] = '0; + // if we got an ITLB miss + if ((|enable_translation_i[HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin + if (&enable_translation_i[HYP_EXT:0] && HYP_EXT==1) begin + ptw_stage_d = G_INTERMED_STAGE; + pptr = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + gptw_pptr_n = pptr; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end else if (!enable_translation_i[0] && HYP_EXT==1) begin + ptw_stage_d = G_FINAL_STAGE; + gpaddr_n = itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end else begin + ptw_stage_d = S_STAGE; + if(enable_translation_i[HYP_EXT*2] && HYP_EXT==1) + ptw_pptr_n = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + else + ptw_pptr_n = {satp_ppn_i[0], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end + + is_instr_ptw_n = 1'b1; + vaddr_n = itlb_vaddr_i; + state_d = WAIT_GRANT; + itlb_miss_o = 1'b1; + + for (int unsigned b=0; b < HYP_EXT+1; b++) begin + tlb_update_asid_n[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + end + + // we got an DTLB miss + end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & dtlb_access_i & ~dtlb_hit_i) begin + if (&en_ld_st_translation_i[HYP_EXT:0] && HYP_EXT==1) begin + ptw_stage_d = G_INTERMED_STAGE; + pptr = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + gptw_pptr_n = pptr; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end else if (!en_ld_st_translation_i[0] && HYP_EXT==1) begin + ptw_stage_d = G_FINAL_STAGE; + gpaddr_n = dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end else begin + ptw_stage_d = S_STAGE; + if(en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1) + ptw_pptr_n = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + else + ptw_pptr_n = {satp_ppn_i[0], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + end + + vaddr_n = dtlb_vaddr_i; + state_d = WAIT_GRANT; + dtlb_miss_o = 1'b1; + + for (int unsigned b=0; b < HYP_EXT+1; b++) begin + tlb_update_asid_n[b] = b==0 ? (en_ld_st_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + end + 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; + + 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 - // 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-1) begin - // Should already be the last level page table => Error - ptw_lvl_n = PT_LEVELS-1; - state_d = PROPAGATE_ERROR; + + PTE_LOOKUP: begin + // we wait for the valid signal + if (data_rvalid_q) begin + + // check if the global mapping bit is set + if (pte[0].g && ptw_stage_q == S_STAGE) + 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[0].v || (!pte[0].r && pte[0].w))// || (|pte.reserved)) + state_d = PROPAGATE_ERROR; + // ----------- + // Valid PTE + // ----------- + else begin + state_d = IDLE; + // it is a valid PTE + // if pte.r = 1 or pte.x = 1 it is a valid PTE + if (pte[0].r || pte[0].x) begin + case (ptw_stage_q) + S_STAGE: begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + state_d = WAIT_GRANT; + ptw_stage_d = G_FINAL_STAGE; + if(HYP_EXT==1) + pte[HYP_EXT*2] = pte[0]; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; + // gpaddr = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; + // if (ptw_lvl_q[0] == 1) + // gpaddr[20:0] = vaddr_q[20:0]; + // if(ptw_lvl_q[0] == 0) + // gpaddr[29:0] = vaddr_q[29:0]; + gpaddr_n = gpaddr[ptw_lvl_q[0]]; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_lvl_n[0] = 0; + end + end + G_INTERMED_STAGE: begin + state_d = WAIT_GRANT; + ptw_stage_d = S_STAGE; + ptw_lvl_n[0] = ptw_lvl_q[HYP_EXT]; + pptr = {pte[0].ppn[riscv::GPPNW-1:0], gptw_pptr_q[11:0]}; + if (ptw_lvl_q[0] == 1) + pptr[20:0] = gptw_pptr_q[20:0]; + if(ptw_lvl_q[0] == 0) + pptr[29:0] = gptw_pptr_q[29:0]; + ptw_pptr_n = pptr; + end + default:; + endcase + // Valid translation found (either 1G, 2M 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[0].x || !pte[0].a) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) + itlb_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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin + if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) + dtlb_update_o.valid = 1'b1; + end else begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + 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[0].w || !pte[0].d)) begin + dtlb_update_o.valid = 1'b0; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + end + + //if there is a misaligned page, propagate error + if (|misaligned_page) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + dtlb_update_o.valid = 1'b0; + itlb_update_o.valid = 1'b0; + end + + // check if 63:41 are all zeros + if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = G_FINAL_STAGE; + end + // this is a pointer to the next TLB level + end else begin + // pointer to next level of page table + + if (ptw_lvl_q[0] == PT_LEVELS-1) begin + // Should already be the last level page table => Error + ptw_lvl_n[0] = PT_LEVELS-1; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + + + end else begin + ptw_lvl_n[0] = ptw_lvl_q[0]+1; + state_d = WAIT_GRANT; + + case (ptw_stage_q) + S_STAGE: begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + ptw_stage_d = G_INTERMED_STAGE; + if(HYP_EXT==1) + pte[HYP_EXT*2] = pte[0]; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]+1; + pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + gptw_pptr_n = pptr; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_lvl_n[0] = 0; + end else begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + end + G_INTERMED_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + G_FINAL_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + endcase + + if(HYP_EXT==1 && (pte[0].a || pte[0].d || pte[0].u)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + + end + + // check if 63:41 are all zeros + if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + end + end + + // Check if this access was actually allowed from a PMP perspective + if (!allow_access) begin + itlb_update_o.valid = 1'b0; + dtlb_update_o.valid = 1'b0; + // we have to return the failed address in bad_addr + ptw_pptr_n = ptw_pptr_q; + ptw_stage_d = ptw_stage_q; + state_d = PROPAGATE_ACCESS_ERROR; + end 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], (PT_LEVELS)'(0)}; - state_d = WAIT_GRANT; - // 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 = IDLE; + ptw_error_o[0] = 1'b1; + if(HYP_EXT==1) begin + ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; + ptw_error_o[HYP_EXT*2] = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0; end - end + end + PROPAGATE_ACCESS_ERROR: begin + state_d = IDLE; + 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 + 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 = IDLE; 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; + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + state_q <= IDLE; + ptw_stage_q <= S_STAGE; + is_instr_ptw_q <= 1'b0; + ptw_lvl_q <= '0; + tag_valid_q <= 1'b0; + tlb_update_asid_q <= '0; + vaddr_q <= '0; + gpaddr_q <= '0; + ptw_pptr_q <= '0; + gptw_pptr_q <= '0; + global_mapping_q <= 1'b0; + data_rdata_q <= '0; + data_rvalid_q <= 1'b0; + if(HYP_EXT==1) + pte[HYP_EXT] = '0; + end else begin + state_q <= state_d; + ptw_stage_q <= ptw_stage_d; + ptw_pptr_q <= ptw_pptr_n; + gptw_pptr_q <= gptw_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; + gpaddr_q <= gpaddr_n; + global_mapping_q <= global_mapping_n; + data_rdata_q <= req_port_i.data_rdata; + data_rvalid_q <= req_port_i.data_rvalid; + + if(HYP_EXT==1) + pte[HYP_EXT] = pte[HYP_EXT*2]; 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 - -//for simulation purposes -// 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 <= 0; - 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 */ \ No newline at end of file +/* verilator lint_on WIDTH */ From 708b8c2c6b229dbe6318cf31e7edb87300e4f536 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 2 Feb 2024 17:01:36 +0100 Subject: [PATCH 078/182] fix data padding for all configurations --- core/mmu_unify/cva6_ptw.sv | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index bbf43eaffe..588585f10f 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -311,19 +311,19 @@ module cva6_ptw import ariane_pkg::*; #( if ((|enable_translation_i[HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin if (&enable_translation_i[HYP_EXT:0] && HYP_EXT==1) begin ptw_stage_d = G_INTERMED_STAGE; - pptr = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + pptr = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else if (!enable_translation_i[0] && HYP_EXT==1) begin ptw_stage_d = G_FINAL_STAGE; gpaddr_n = itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else begin ptw_stage_d = S_STAGE; if(enable_translation_i[HYP_EXT*2] && HYP_EXT==1) - ptw_pptr_n = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; else - ptw_pptr_n = {satp_ppn_i[0], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[0], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end is_instr_ptw_n = 1'b1; @@ -339,19 +339,19 @@ module cva6_ptw import ariane_pkg::*; #( end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & dtlb_access_i & ~dtlb_hit_i) begin if (&en_ld_st_translation_i[HYP_EXT:0] && HYP_EXT==1) begin ptw_stage_d = G_INTERMED_STAGE; - pptr = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + pptr = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else if (!en_ld_st_translation_i[0] && HYP_EXT==1) begin ptw_stage_d = G_FINAL_STAGE; gpaddr_n = dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else begin ptw_stage_d = S_STAGE; if(en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1) - ptw_pptr_n = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; else - ptw_pptr_n = {satp_ppn_i[0], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[0], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end vaddr_n = dtlb_vaddr_i; @@ -411,7 +411,7 @@ module cva6_ptw import ariane_pkg::*; #( // if(ptw_lvl_q[0] == 0) // gpaddr[29:0] = vaddr_q[29:0]; gpaddr_n = gpaddr[ptw_lvl_q[0]]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; ptw_lvl_n[0] = 0; end end @@ -505,7 +505,7 @@ module cva6_ptw import ariane_pkg::*; #( ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]+1; pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], 3'b0}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; ptw_lvl_n[0] = 0; end else begin ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; From 2b8ec8657d1a13e857d489957b202957d840d953 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 5 Feb 2024 18:08:52 +0100 Subject: [PATCH 079/182] shared tlb pass tests --- core/mmu_unify/cva6_mmu.sv | 162 ++++++--- core/mmu_unify/cva6_ptw.sv | 171 ++++----- core/mmu_unify/cva6_shared_tlb.sv | 570 ++++++++++++++++-------------- 3 files changed, 477 insertions(+), 426 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f369b9d8d5..b522dd0b7f 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -127,7 +127,7 @@ module cva6_mmu import ariane_pkg::*; #( logic ptw_access_exception; // PTW threw an access exception (PMPs) logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - logic [riscv::VLEN-1:0] update_vaddr; + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr;; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; @@ -145,6 +145,9 @@ module cva6_mmu import ariane_pkg::*; #( logic dtlb_lu_hit; logic [riscv::GPLEN-1:0] dtlb_gpaddr; + logic [HYP_EXT*2:0] shared_tlb_access; + logic shared_tlb_hit,itlb_req; + // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; @@ -163,7 +166,8 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( flush_tlb_i ), - .v_st_enbl_i ( enable_translation_i ), + // .v_st_enbl_i ( enable_translation_i ), + .v_st_enbl_i('1), .update_i ( update_itlb ), .lu_access_i ( itlb_lu_access ), .lu_asid_i ( itlb_mmu_asid_i ), @@ -189,7 +193,8 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( flush_tlb_i ), - .v_st_enbl_i ( en_ld_st_translation_i ), + // .v_st_enbl_i ( en_ld_st_translation_i ), + .v_st_enbl_i('1), .update_i ( update_dtlb ), .lu_access_i ( dtlb_lu_access ), .lu_asid_i ( dtlb_mmu_asid_i ), @@ -203,51 +208,99 @@ module cva6_mmu import ariane_pkg::*; #( ); - cva6_ptw #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT(HYP_EXT), - .ASID_WIDTH ( ASID_WIDTH ), - .VPN_LEN(VPN_LEN), - .CVA6Cfg (CVA6Cfg), - .PT_LEVELS(PT_LEVELS) - ) 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 ), - .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o ( update_vaddr ), - .itlb_update_o ( update_itlb ), - .dtlb_update_o ( update_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 ), - .hlvx_inst_i ( hlvx_inst_i ), - - .req_port_i ( req_port_i ), - .req_port_o ( req_port_o ), - .mxr_i (mxr_i ), - - .satp_ppn_i (satp_ppn_i ), - - .pmpcfg_i, - .pmpaddr_i, - - .bad_paddr_o ( ptw_bad_paddr ), - .* + cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .ASID_WIDTH (ASID_WIDTH), + .ASID_LEN (ASID_LEN), + .VPN_LEN (VPN_LEN), + .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), + .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 #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT(HYP_EXT), + .ASID_WIDTH ( ASID_WIDTH ), + .VPN_LEN(VPN_LEN), + .CVA6Cfg ( CVA6Cfg ), + .PT_LEVELS(PT_LEVELS) + ) 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 ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o ( update_vaddr ), + .shared_tlb_update_o(update_shared_tlb), + + .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), + + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i ( hlvx_inst_i ), + + .req_port_i ( req_port_i ), + .req_port_o ( req_port_o ), + .mxr_i (mxr_i ), + // Performance counters + .shared_tlb_miss_o(), //open for now + + .satp_ppn_i (satp_ppn_i ), + + .pmpcfg_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}), @@ -303,11 +356,11 @@ module cva6_mmu import ariane_pkg::*; #( // 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[0] = icache_areq_i.fetch_req && enable_translation_i[0] && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) + iaccess_err[0] = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && mmu_itlb_content[0].u)); if(HYP_EXT==1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !mmu_itlb_content[HYP_EXT].u; + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && !mmu_itlb_content[HYP_EXT].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 @@ -389,7 +442,7 @@ module cva6_mmu import ariane_pkg::*; #( if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -586,12 +639,11 @@ module cva6_mmu import ariane_pkg::*; #( // Check if the User flag is set, then we may only access it in supervisor mode // if SUM is enabled - daccess_err[0] = en_ld_st_translation_i[0] && - ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].u) || // SUM is not set and we are trying to access a user page in supervisor mode + daccess_err[0] = ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); if(HYP_EXT==1) - daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + daccess_err[HYP_EXT] = !dtlb_pte_q[1].u; // translation is enabled and no misaligned exception occurred if ((|en_ld_st_translation_i[HYP_EXT:0]) && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; @@ -610,7 +662,7 @@ module cva6_mmu import ariane_pkg::*; #( 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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + if(HYP_EXT==1 && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, @@ -619,7 +671,7 @@ module cva6_mmu import ariane_pkg::*; #( en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else if (en_ld_st_translation_i[0] && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + end else if ((!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 588585f10f..ac66eb4129 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -22,7 +22,7 @@ module cva6_ptw import ariane_pkg::*; #( parameter int unsigned HYP_EXT = 0, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, parameter int unsigned VPN_LEN = 1, - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, // Clock @@ -35,9 +35,6 @@ module cva6_ptw import ariane_pkg::*; #( output logic [HYP_EXT*2:0] ptw_error_o, // set when an error occurred output logic ptw_access_exception_o, // set when an PMP access exception occured - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - input logic hlvx_inst_i, // is a HLVX load/store instruction input logic lsu_is_store_i, // this translation was triggered by a store @@ -47,8 +44,7 @@ module cva6_ptw import ariane_pkg::*; #( // to TLBs, update logic - output tlb_update_cva6_t itlb_update_o, - output tlb_update_cva6_t dtlb_update_o, + output tlb_update_cva6_t shared_tlb_update_o, output logic [riscv::VLEN-1:0] update_vaddr_o, @@ -56,20 +52,19 @@ module cva6_ptw import ariane_pkg::*; #( // 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 [HYP_EXT*2:0] 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, - input logic dtlb_access_i, - input logic dtlb_hit_i, - input logic [riscv::VLEN-1:0] dtlb_vaddr_i, // from CSR file input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] input logic [HYP_EXT:0] mxr_i, // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + output logic shared_tlb_miss_o, + // PMP input riscv::pmpcfg_t [15:0] pmpcfg_i, @@ -94,7 +89,8 @@ module cva6_ptw import ariane_pkg::*; #( PTE_LOOKUP, WAIT_RVALID, PROPAGATE_ERROR, - PROPAGATE_ACCESS_ERROR + PROPAGATE_ACCESS_ERROR, + LATENCY } state_q, state_d; logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_q, ptw_lvl_n; @@ -174,42 +170,28 @@ module cva6_ptw import ariane_pkg::*; #( always_comb begin : tlb_update - itlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); - dtlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); // update the correct page table level for (int unsigned y=0; y < HYP_EXT+1; y++) begin for (int unsigned x=0; x < PT_LEVELS-1; x++) begin - if(&enable_translation_i[HYP_EXT:0] && HYP_EXT==1) - itlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); - else if(enable_translation_i[0]) - itlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; - else - itlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; - - if(&en_ld_st_translation_i[HYP_EXT:0]) - dtlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); - else if(en_ld_st_translation_i[0]) - dtlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; + if(&shared_tlb_access_i[HYP_EXT:0] && HYP_EXT==1) + shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); + else if(shared_tlb_access_i[0]) + shared_tlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; else - dtlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; + shared_tlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; end // set the global mapping bit - if(enable_translation_i[HYP_EXT] && HYP_EXT==1) begin - itlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; - end else begin - itlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; - end - if(en_ld_st_translation_i[HYP_EXT] && HYP_EXT==1) begin - dtlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; + if(shared_tlb_access_i[HYP_EXT] && HYP_EXT==1) begin + shared_tlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; end else begin - dtlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; + shared_tlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; end end // output the correct ASIDs - itlb_update_o.asid = tlb_update_asid_q; - dtlb_update_o.asid = tlb_update_asid_q; + shared_tlb_update_o.asid = tlb_update_asid_q; bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; if(HYP_EXT==1) @@ -220,7 +202,7 @@ module cva6_ptw import ariane_pkg::*; #( logic allow_access; - + pmp #( .PLEN ( riscv::PLEN ), @@ -238,6 +220,11 @@ module cva6_ptw import ariane_pkg::*; #( .allow_o ( allow_access ) ); + + 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 //------------------- @@ -269,13 +256,11 @@ module cva6_ptw import ariane_pkg::*; #( // PTW memory interface tag_valid_n = 1'b0; req_port_o.data_req = 1'b0; - req_port_o.data_be = 8'hFF; - req_port_o.data_size = 2'b11; + req_port_o.data_size = 2'(PT_LEVELS); req_port_o.data_we = 1'b0; ptw_error_o = '0; ptw_access_exception_o = 1'b0; - itlb_update_o.valid = 1'b0; - dtlb_update_o.valid = 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; @@ -290,8 +275,7 @@ module cva6_ptw import ariane_pkg::*; #( pptr = ptw_pptr_q; // gpaddr = gpaddr_q; - itlb_miss_o = 1'b0; - dtlb_miss_o = 1'b0; + shared_tlb_miss_o = 1'b0; if(HYP_EXT==1) pte[HYP_EXT*2] = pte[HYP_EXT]; @@ -308,58 +292,31 @@ module cva6_ptw import ariane_pkg::*; #( if(HYP_EXT==1) pte[HYP_EXT*2] = '0; // if we got an ITLB miss - if ((|enable_translation_i[HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin - if (&enable_translation_i[HYP_EXT:0] && HYP_EXT==1) begin + if (|shared_tlb_access_i & ~shared_tlb_hit_i) begin + if (&shared_tlb_access_i[HYP_EXT:0] && HYP_EXT==1) begin ptw_stage_d = G_INTERMED_STAGE; - pptr = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; + pptr = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; gptw_pptr_n = pptr; ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else if (!enable_translation_i[0] && HYP_EXT==1) begin + end else if (!shared_tlb_access_i[0] && HYP_EXT==1) begin ptw_stage_d = G_FINAL_STAGE; - gpaddr_n = itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], itlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; + gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else begin ptw_stage_d = S_STAGE; - if(enable_translation_i[HYP_EXT*2] && HYP_EXT==1) - ptw_pptr_n = {satp_ppn_i[HYP_EXT], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; + if(shared_tlb_access_i[HYP_EXT*2] && HYP_EXT==1) + ptw_pptr_n = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; else - ptw_pptr_n = {satp_ppn_i[0], itlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; + ptw_pptr_n = {satp_ppn_i[0], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end - is_instr_ptw_n = 1'b1; - vaddr_n = itlb_vaddr_i; + is_instr_ptw_n = itlb_req_i; + vaddr_n = shared_tlb_vaddr_i; state_d = WAIT_GRANT; - itlb_miss_o = 1'b1; + shared_tlb_miss_o = 1'b1; for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_n[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; - end - - // we got an DTLB miss - end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & dtlb_access_i & ~dtlb_hit_i) begin - if (&en_ld_st_translation_i[HYP_EXT:0] && HYP_EXT==1) begin - ptw_stage_d = G_INTERMED_STAGE; - pptr = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else if (!en_ld_st_translation_i[0] && HYP_EXT==1) begin - ptw_stage_d = G_FINAL_STAGE; - gpaddr_n = dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], dtlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else begin - ptw_stage_d = S_STAGE; - if(en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1) - ptw_pptr_n = {satp_ppn_i[HYP_EXT], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - else - ptw_pptr_n = {satp_ppn_i[0], dtlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end - - vaddr_n = dtlb_vaddr_i; - state_d = WAIT_GRANT; - dtlb_miss_o = 1'b1; - - for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_n[b] = b==0 ? (en_ld_st_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + tlb_update_asid_n[b] = b==0 ? (shared_tlb_access_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; end end end @@ -393,25 +350,20 @@ module cva6_ptw import ariane_pkg::*; #( // 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[0].r || pte[0].x) begin case (ptw_stage_q) S_STAGE: begin - if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT]) begin state_d = WAIT_GRANT; ptw_stage_d = G_FINAL_STAGE; if(HYP_EXT==1) pte[HYP_EXT*2] = pte[0]; ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; - // gpaddr = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; - // if (ptw_lvl_q[0] == 1) - // gpaddr[20:0] = vaddr_q[20:0]; - // if(ptw_lvl_q[0] == 0) - // gpaddr[29:0] = vaddr_q[29:0]; gpaddr_n = gpaddr[ptw_lvl_q[0]]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; + ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)],(PT_LEVELS)'(0)}; ptw_lvl_n[0] = 0; end end @@ -439,8 +391,8 @@ module cva6_ptw import ariane_pkg::*; #( if (!pte[0].x || !pte[0].a) begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; - end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) - itlb_update_o.valid = 1'b1; + end else if((ptw_stage_q == G_FINAL_STAGE) || !shared_tlb_access_i[HYP_EXT] || HYP_EXT==0) + shared_tlb_update_o.valid = 1'b1; end else begin // ------------ @@ -451,9 +403,9 @@ module cva6_ptw import ariane_pkg::*; #( // 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin - if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) - dtlb_update_o.valid = 1'b1; + if (pte[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && shared_tlb_access_i[HYP_EXT*2] && HYP_EXT==1))))) begin + if((ptw_stage_q == G_FINAL_STAGE) || !shared_tlb_access_i[HYP_EXT] || HYP_EXT==0) + shared_tlb_update_o.valid = 1'b1; end else begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; @@ -462,7 +414,7 @@ module cva6_ptw import ariane_pkg::*; #( // 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[0].w || !pte[0].d)) begin - dtlb_update_o.valid = 1'b0; + shared_tlb_update_o.valid = 1'b0; state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; end @@ -472,12 +424,11 @@ module cva6_ptw import ariane_pkg::*; #( if (|misaligned_page) begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; - dtlb_update_o.valid = 1'b0; - itlb_update_o.valid = 1'b0; + shared_tlb_update_o.valid = 1'b0; end // check if 63:41 are all zeros - if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin + if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT*2] && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin state_d = PROPAGATE_ERROR; ptw_stage_d = G_FINAL_STAGE; end @@ -498,7 +449,7 @@ module cva6_ptw import ariane_pkg::*; #( case (ptw_stage_q) S_STAGE: begin - if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT]) begin ptw_stage_d = G_INTERMED_STAGE; if(HYP_EXT==1) pte[HYP_EXT*2] = pte[0]; @@ -527,7 +478,7 @@ module cva6_ptw import ariane_pkg::*; #( end // check if 63:41 are all zeros - if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin + if (HYP_EXT==1 && (shared_tlb_access_i[HYP_EXT*2] && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; end @@ -536,8 +487,7 @@ module cva6_ptw import ariane_pkg::*; #( // Check if this access was actually allowed from a PMP perspective if (!allow_access) begin - itlb_update_o.valid = 1'b0; - dtlb_update_o.valid = 1'b0; + shared_tlb_update_o.valid = 1'b0; // we have to return the failed address in bad_addr ptw_pptr_n = ptw_pptr_q; ptw_stage_d = ptw_stage_q; @@ -556,7 +506,7 @@ module cva6_ptw import ariane_pkg::*; #( end end PROPAGATE_ACCESS_ERROR: begin - state_d = IDLE; + state_d = LATENCY; ptw_access_exception_o = 1'b1; end // wait for the rvalid before going back to IDLE @@ -564,6 +514,9 @@ module cva6_ptw import ariane_pkg::*; #( if (data_rvalid_q) state_d = IDLE; end + LATENCY: begin + state_d = IDLE; + end default: begin state_d = IDLE; end @@ -581,7 +534,7 @@ module cva6_ptw import ariane_pkg::*; #( 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 = IDLE; + state_d = LATENCY; end end diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 8787104d79..1473677d51 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -14,6 +14,11 @@ // Description: N-way associative shared TLB, it allows to reduce the number // of ITLB and DTLB entries. // +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2024-02-05 0.2 A.Gonzalez Generic shared TLB for CVA6 with Hypervisor support +// =========================================================================== // /* verilator lint_off WIDTH */ @@ -22,49 +27,50 @@ 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, - parameter int ASID_WIDTH = 1, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] 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, + 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 enable_translation_i, // CSRs indicate to enable ??? - input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] - input logic dtlb_access_i, - input logic dtlb_hit_i, - input logic [riscv::VLEN-1:0] dtlb_vaddr_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, - // to TLBs, update logic - output tlb_update_cva6_t itlb_update_o, - output tlb_update_cva6_t dtlb_update_o, + input logic dtlb_access_i, + input logic dtlb_hit_i, + input logic [riscv::VLEN-1:0] dtlb_vaddr_i, - // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + // to TLBs, update logic + output tlb_update_cva6_t itlb_update_o, + output tlb_update_cva6_t dtlb_update_o, - output logic shared_tlb_access_o, - output logic shared_tlb_hit_o, - output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, + // Performance counters + output logic itlb_miss_o, + output logic dtlb_miss_o, - output logic itlb_req_o, + output logic [HYP_EXT*2:0] shared_tlb_access_o, + output logic shared_tlb_hit_o, + output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, - // Update shared TLB in case of miss - input tlb_update_cva6_t shared_tlb_update_i + output logic itlb_req_o, + + // Update shared TLB in case of miss + input tlb_update_cva6_t shared_tlb_update_i ); @@ -77,9 +83,10 @@ function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(S 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; +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 } shared_tag_t; shared_tag_t shared_tag_wr; @@ -115,17 +122,19 @@ 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 [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; +logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] page_match; +logic [SHARED_TLB_WAYS-1:0][HYP_EXT:0] match_asid; +logic [SHARED_TLB_WAYS-1:0] match_stage; -pte_cva6_t [SHARED_TLB_WAYS-1:0] pte; +pte_cva6_t [SHARED_TLB_WAYS-1:0][HYP_EXT: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 [ASID_WIDTH[0]-1:0] tlb_update_asid_q [HYP_EXT:0], tlb_update_asid_d[HYP_EXT:0]; -logic shared_tlb_access_q, shared_tlb_access_d; +logic [HYP_EXT*2:0] 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; @@ -148,251 +157,288 @@ assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; assign itlb_req_o = itlb_req_q; genvar i,x; - generate +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 + //identify page_match for all TLB Entries + assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 + &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~enable_translation_i[HYP_EXT:0])): + ((&enable_translation_i[HYP_EXT:0]) ? + ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) + || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): + shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && enable_translation_i[0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && enable_translation_i[HYP_EXT])); + + //identify if vpn matches at all PT levels for all TLB entries + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~enable_translation_i[0]) ? // + vpn_q[x] == shared_tag_rd[i].vpn[x] && shared_tag_rd[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // + vpn_q[x] == shared_tag_rd[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]; - 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 - end - endgenerate + end + end +endgenerate - genvar w; +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) ? // + assign vpn_d[w] = (enable_translation_i[0] & 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)? // + ((en_ld_st_translation_i[0] & 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 - + if(HYP_EXT==1) + //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 + assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = (enable_translation_i[0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + itlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : // + ((en_ld_st_translation_i[0] & dtlb_access_i & ~dtlb_hit_i)? // + dtlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : vpn_q[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]); + /////////////////////////////////////////////////////// // tag comparison, hit generation /////////////////////////////////////////////////////// -always_comb begin : itlb_dtlb_miss - itlb_miss_o = 1'b0; - dtlb_miss_o = 1'b0; - - 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)]; - - 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)]; - - 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; - //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; - 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]; + always_comb begin : itlb_dtlb_miss + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; + + 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[HYP_EXT:0]) & 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)]; + + itlb_miss_o = 1'b1; + itlb_req_d = 1'b1; + + // tlb_update_asid_d = asid_i; + + for (int unsigned b=0; b < HYP_EXT+1; b++) begin + tlb_update_asid_d[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + end + + shared_tlb_access_d = enable_translation_i; + shared_tlb_vaddr_d = itlb_vaddr_i; + + // we got an DTLB miss + end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & 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; + + for (int unsigned b=0; b < HYP_EXT+1; b++) begin + tlb_update_asid_d[b] = b==0 ? (en_ld_st_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + end + + shared_tlb_access_d = en_ld_st_translation_i; + 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 + // first level match, this may be a giga 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) + match_asid[i][0] = (((asid_i[0] == shared_tag_rd[i].asid[0]) || pte[i][0].g) && enable_translation_i[0]) || !enable_translation_i[0]; + + if(HYP_EXT==1) begin + match_asid[i][HYP_EXT] = (asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && enable_translation_i[HYP_EXT]) || !enable_translation_i[HYP_EXT]; + end + + // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off + match_stage[i] = shared_tag_rd[i].v_st_enbl == enable_translation_i; + + if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin + if (|level_match[i]) 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]; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + itlb_update_o.asid[a] = tlb_update_asid_q[a]; + end + 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]; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + dtlb_update_o.asid[a] = tlb_update_asid_q[a]; + end + end + end + end + end //tag_comparison 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; -// 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; - - -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 - - -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) -); + // 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 <= '{default: 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 -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) -); + // ------------------ + // 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; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; + assign shared_tag_wr.v_st_enbl = enable_translation_i; + + 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 + + + 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(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] = pte_cva6_t'(pte_rd_data[i]); -end -endmodule - -/* verilator lint_on WIDTH */ \ No newline at end of file + 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(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] = pte_cva6_t'(pte_rd_data[i]); + end + +endmodule \ No newline at end of file From 165bfc2ab2e48730aab484ea790408848254c163 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Tue, 6 Feb 2024 16:14:55 +0100 Subject: [PATCH 080/182] attempt shared TLB --- core/mmu_unify/cva6_mmu.sv | 91 +++++++++++--------- core/mmu_unify/cva6_ptw.sv | 17 ++-- core/mmu_unify/cva6_shared_tlb.sv | 136 +++++++++++++++--------------- 3 files changed, 127 insertions(+), 117 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index b522dd0b7f..7934e9fb86 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -127,19 +127,19 @@ module cva6_mmu import ariane_pkg::*; #( logic ptw_access_exception; // PTW threw an access exception (PMPs) logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr;; + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; logic itlb_lu_access; - pte_cva6_t [HYP_EXT:0] mmu_itlb_content ; + pte_cva6_t [HYP_EXT:0] itlb_content ; logic [PT_LEVELS-2:0] itlb_is_page; logic itlb_lu_hit; logic [riscv::GPLEN-1:0] itlb_gpaddr; logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; logic dtlb_lu_access; - pte_cva6_t [HYP_EXT:0] mmu_dtlb_content ; + pte_cva6_t [HYP_EXT:0] dtlb_content ; logic [PT_LEVELS-2:0] dtlb_is_page; logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; logic dtlb_lu_hit; @@ -151,12 +151,13 @@ module cva6_mmu import ariane_pkg::*; #( // Assignments assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; + assign dtlb_lu_access = lsu_req_i; + cva6_tlb #( .pte_cva6_t(pte_cva6_t), .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES), .HYP_EXT(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -174,7 +175,7 @@ module cva6_mmu import ariane_pkg::*; #( .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 ( mmu_itlb_content ), + .lu_content_o ( itlb_content ), .lu_gpaddr_o ( itlb_gpaddr ), .lu_is_page_o ( itlb_is_page ), .lu_hit_o ( itlb_lu_hit ) @@ -183,7 +184,7 @@ module cva6_mmu import ariane_pkg::*; #( cva6_tlb #( .pte_cva6_t(pte_cva6_t), .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES ), + .TLB_ENTRIES (DATA_TLB_ENTRIES), .HYP_EXT(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), @@ -201,8 +202,8 @@ module cva6_mmu import ariane_pkg::*; #( .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 ( mmu_dtlb_content ), - .lu_gpaddr_o ( dtlb_gpaddr ), + .lu_content_o ( dtlb_content ), + .lu_gpaddr_o ( dtlb_gpaddr ), .lu_is_page_o ( dtlb_is_page ), .lu_hit_o ( dtlb_lu_hit ) ); @@ -221,7 +222,7 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i (clk_i), .rst_ni (rst_ni), .flush_i(flush_tlb_i), - + .v_st_enbl_i('1), .enable_translation_i (enable_translation_i), .en_ld_st_translation_i(en_ld_st_translation_i), @@ -254,51 +255,60 @@ module cva6_mmu import ariane_pkg::*; #( ); cva6_ptw #( + .CVA6Cfg (CVA6Cfg), .pte_cva6_t(pte_cva6_t), .tlb_update_cva6_t(tlb_update_cva6_t), .HYP_EXT(HYP_EXT), .ASID_WIDTH ( ASID_WIDTH ), .VPN_LEN(VPN_LEN), - .CVA6Cfg ( CVA6Cfg ), .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 ), // .enable_translation_i ( enable_translation_i ), // .en_ld_st_translation_i ( en_ld_st_translation_i), .asid_i (asid_i), .update_vaddr_o ( update_vaddr ), + + // to Shared TLB, update logic .shared_tlb_update_o(update_shared_tlb), + + // 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), - // .dtlb_access_i ( dtlb_lu_access ), // .dtlb_hit_i ( dtlb_lu_hit ), // .dtlb_vaddr_i ( lsu_vaddr_i ), .hlvx_inst_i ( hlvx_inst_i ), - - .req_port_i ( req_port_i ), - .req_port_o ( req_port_o ), + // from CSR file + .satp_ppn_i (satp_ppn_i ), .mxr_i (mxr_i ), + // Performance counters .shared_tlb_miss_o(), //open for now - .satp_ppn_i (satp_ppn_i ), - - .pmpcfg_i, - .pmpaddr_i, - - .bad_paddr_o ( ptw_bad_paddr ), - .* + // PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) + ); // ila_1 i_ila_1 ( @@ -330,8 +340,8 @@ module cva6_mmu import ariane_pkg::*; #( 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : - mmu_itlb_content[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 @@ -339,8 +349,8 @@ module cva6_mmu import ariane_pkg::*; #( 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[HYP_EXT:0] && (|itlb_is_page[a:0]==0)) ? // - (enable_translation_i[HYP_EXT] ? mmu_itlb_content[HYP_EXT].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)))]: - mmu_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)))]) : // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 @@ -356,11 +366,11 @@ module cva6_mmu import ariane_pkg::*; #( // 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[0] = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~mmu_itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && mmu_itlb_content[0].u)); + iaccess_err[0] = 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)); if(HYP_EXT==1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && !mmu_itlb_content[HYP_EXT].u; + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && !itlb_content[HYP_EXT].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 @@ -393,7 +403,7 @@ module cva6_mmu import ariane_pkg::*; #( icache_areq_o.fetch_valid = 1'b0; // // 4K page - // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? mmu_itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; + // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; // // Mega page // if (itlb_is_2M) begin // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; @@ -512,7 +522,7 @@ module cva6_mmu import ariane_pkg::*; #( 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[0]) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if ((!match_any_execute_region) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -580,8 +590,8 @@ module cva6_mmu import ariane_pkg::*; #( assign lsu_dtlb_ppn_o [11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[11:0]: - mmu_dtlb_content[0].ppn[11:0]) : // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + dtlb_content[0].ppn[11:0]) : // lsu_vaddr_n[0][23:12]; genvar i; @@ -596,16 +606,16 @@ module cva6_mmu import ariane_pkg::*; #( assign lsu_dtlb_ppn_o[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]: - mmu_dtlb_content[0].ppn[PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] ): // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (en_ld_st_translation_i[HYP_EXT] ? mmu_dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - mmu_dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; end @@ -620,7 +630,7 @@ module cva6_mmu import ariane_pkg::*; #( lsu_req_n = lsu_req_i; hs_ld_st_inst_n = hs_ld_st_inst_i; misaligned_ex_n = misaligned_ex_i; - dtlb_pte_n = mmu_dtlb_content; + 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; @@ -863,9 +873,8 @@ module cva6_mmu import ariane_pkg::*; #( 1'b1 }; end - else begin + else 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 end else begin if(HYP_EXT==1) begin lsu_exception_o = { @@ -913,7 +922,7 @@ module cva6_mmu import ariane_pkg::*; #( dtlb_pte_q <= '0; dtlb_hit_q <= '0; lsu_is_store_q <= '0; - dtlb_is_page_q <= '0; + dtlb_is_page_q <= '0; end else begin lsu_vaddr_q <= lsu_vaddr_n; lsu_tinst_q <= lsu_tinst_n; diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index ac66eb4129..02a1949323 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -75,9 +75,8 @@ module cva6_ptw import ariane_pkg::*; #( // input registers logic data_rvalid_q; - riscv::xlen_t data_rdata_q; + riscv::xlen_t data_rdata_q; - logic [PT_LEVELS-1:0] misaligned_page; pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] // register to perform context switch between stages // pte_cva6_t gpte_q, gpte_d; @@ -93,7 +92,8 @@ module cva6_ptw import ariane_pkg::*; #( LATENCY } state_q, state_d; - logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_q, ptw_lvl_n; + logic [PT_LEVELS-1:0] misaligned_page; + logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_n,ptw_lvl_q; // define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE // S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs @@ -112,7 +112,6 @@ module cva6_ptw import ariane_pkg::*; #( logic tag_valid_n, tag_valid_q; // register the ASIDs logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; - // register the VPN we need to walk, SV39 defines a 39 bit virtual address logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; @@ -147,7 +146,7 @@ module cva6_ptw import ariane_pkg::*; #( genvar z,w; generate for (z=0; z < PT_LEVELS-1; z++) begin - + // 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. @@ -177,7 +176,7 @@ module cva6_ptw import ariane_pkg::*; #( for (int unsigned x=0; x < PT_LEVELS-1; x++) begin if(&shared_tlb_access_i[HYP_EXT:0] && HYP_EXT==1) shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); - else if(shared_tlb_access_i[0]) + else if(shared_tlb_access_i[0] || HYP_EXT==0) shared_tlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; else shared_tlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; @@ -407,8 +406,8 @@ module cva6_ptw import ariane_pkg::*; #( if((ptw_stage_q == G_FINAL_STAGE) || !shared_tlb_access_i[HYP_EXT] || HYP_EXT==0) shared_tlb_update_o.valid = 1'b1; end else begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; 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 @@ -531,7 +530,7 @@ module cva6_ptw import ariane_pkg::*; #( // 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)) + 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; diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 1473677d51..002314bc1b 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -38,7 +38,7 @@ import ariane_pkg::*; 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores @@ -122,8 +122,9 @@ 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 [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] vpn_match; -logic [SHARED_TLB_WAYS-1:0][PT_LEVELS-1:0] level_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; + logic [SHARED_TLB_WAYS-1:0][HYP_EXT:0] match_asid; logic [SHARED_TLB_WAYS-1:0] match_stage; @@ -157,19 +158,20 @@ assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; assign itlb_req_o = itlb_req_q; genvar i,x; -generate + generate for (i=0; i < SHARED_TLB_WAYS; i++) begin - 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==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~enable_translation_i[HYP_EXT:0])): - ((&enable_translation_i[HYP_EXT:0]) ? - ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) - || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): - shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && enable_translation_i[0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && enable_translation_i[HYP_EXT])); + //identify page_match for all TLB Entries + + for (x=0; x < PT_LEVELS; x++) begin + assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 + &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[HYP_EXT:0])): + ((&v_st_enbl_i[HYP_EXT:0]) ? + ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) + || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): + shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[HYP_EXT])); //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~enable_translation_i[0]) ? // + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[0]) ? // vpn_q[x] == shared_tag_rd[i].vpn[x] && shared_tag_rd[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // vpn_q[x] == shared_tag_rd[i].vpn[x]; @@ -206,17 +208,17 @@ genvar w; 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; + pte_rd_addr = '0; // if we got an ITLB miss if ((|enable_translation_i[HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin @@ -227,31 +229,30 @@ genvar w; itlb_miss_o = 1'b1; itlb_req_d = 1'b1; - // tlb_update_asid_d = asid_i; for (int unsigned b=0; b < HYP_EXT+1; b++) begin tlb_update_asid_d[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; end - shared_tlb_access_d = enable_translation_i; + shared_tlb_access_d = v_st_enbl_i; shared_tlb_vaddr_d = itlb_vaddr_i; - + // we got an DTLB miss end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & 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; - + for (int unsigned b=0; b < HYP_EXT+1; b++) begin tlb_update_asid_d[b] = b==0 ? (en_ld_st_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; end - - shared_tlb_access_d = en_ld_st_translation_i; + + shared_tlb_access_d = v_st_enbl_i; shared_tlb_vaddr_d = dtlb_vaddr_i; end end //itlb_dtlb_miss @@ -265,14 +266,14 @@ genvar w; for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin // first level match, this may be a giga 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) - match_asid[i][0] = (((asid_i[0] == shared_tag_rd[i].asid[0]) || pte[i][0].g) && enable_translation_i[0]) || !enable_translation_i[0]; + match_asid[i][0] = (((tlb_update_asid_q[0] == shared_tag_rd[i].asid[0]) || pte[i][0].g) && v_st_enbl_i[0]) || !v_st_enbl_i[0]; if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && enable_translation_i[HYP_EXT]) || !enable_translation_i[HYP_EXT]; + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[HYP_EXT]) || !v_st_enbl_i[HYP_EXT]; end // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == enable_translation_i; + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i; if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin if (|level_match[i]) begin @@ -282,20 +283,20 @@ genvar w; 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]; + itlb_update_o.content[0] = pte[i][0]; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin itlb_update_o.asid[a] = tlb_update_asid_q[a]; end - end else if (dtlb_req_q) begin + 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]; + dtlb_update_o.content[0] = pte[i][0]; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin dtlb_update_o.asid[a] = tlb_update_asid_q[a]; end - end + end end end end //tag_comparison @@ -304,27 +305,27 @@ genvar w; // 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 <= '{default: 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; + itlb_vpn_q <= '0; + dtlb_vpn_q <= '0; + tlb_update_asid_q <= '{default: 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]; + 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 @@ -337,21 +338,21 @@ genvar w; pte_wr_en = '0; if (|flush_i) begin - shared_tag_valid_d = '0; + 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 + 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; assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; - assign shared_tag_wr.v_st_enbl = enable_translation_i; + assign shared_tag_wr.v_st_enbl = v_st_enbl_i; genvar z; generate @@ -365,7 +366,7 @@ genvar w; 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; @@ -397,11 +398,11 @@ genvar w; 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 #( @@ -419,9 +420,9 @@ genvar w; .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(pte_cva6_t)), @@ -438,7 +439,8 @@ genvar w; .ruser_o(), .rdata_o(pte_rd_data[i]) ); - assign pte[i] = pte_cva6_t'(pte_rd_data[i]); + assign pte[i][0] = pte_cva6_t'(pte_rd_data[i]); end +endmodule -endmodule \ No newline at end of file +/* verilator lint_on WIDTH */ \ No newline at end of file From 53dded284f196b6b3c7bc7c5dd28b6a9f6da53df Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 7 Feb 2024 10:29:42 +0100 Subject: [PATCH 081/182] fix ptw go to latency instead of idel on error propagation --- core/mmu_unify/cva6_ptw.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 02a1949323..a4f5cf0e07 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -186,7 +186,7 @@ module cva6_ptw import ariane_pkg::*; #( if(shared_tlb_access_i[HYP_EXT] && HYP_EXT==1) begin shared_tlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; end else begin - shared_tlb_update_o.content[y] = y==0 ? pte[0] | (global_mapping_q << 5) : '0; + shared_tlb_update_o.content[y] = y==0 ? (pte[0] | (global_mapping_q << 5)) : '0; end end // output the correct ASIDs @@ -497,7 +497,7 @@ module cva6_ptw import ariane_pkg::*; #( end // Propagate error to MMU/LSU PROPAGATE_ERROR: begin - state_d = IDLE; + state_d = LATENCY; ptw_error_o[0] = 1'b1; if(HYP_EXT==1) begin ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; From 7a747a2608cc8f7b40b5ac6d232f137c237d71de Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 14 Feb 2024 18:03:37 +0100 Subject: [PATCH 082/182] Common mmu, ptw and tlb - OK sv32, sv39 and sv39x4 --- core/mmu_unify/cva6_mmu.sv | 24 +++++++++------------ core/mmu_unify/cva6_ptw.sv | 44 ++++++++++++++++++++------------------ core/mmu_unify/cva6_tlb.sv | 20 ++++++++--------- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 7934e9fb86..23447eeb53 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -146,7 +146,7 @@ module cva6_mmu import ariane_pkg::*; #( logic [riscv::GPLEN-1:0] dtlb_gpaddr; logic [HYP_EXT*2:0] shared_tlb_access; - logic shared_tlb_hit,itlb_req; + logic shared_tlb_hit, itlb_req; // Assignments @@ -212,6 +212,7 @@ module cva6_mmu import ariane_pkg::*; #( cva6_shared_tlb #( .SHARED_TLB_DEPTH (64), .SHARED_TLB_WAYS (2), + .HYP_EXT(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), @@ -222,11 +223,12 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i (clk_i), .rst_ni (rst_ni), .flush_i(flush_tlb_i), - .v_st_enbl_i('1), + .v_st_enbl_i({'1,'1}), .enable_translation_i (enable_translation_i), .en_ld_st_translation_i(en_ld_st_translation_i), - .asid_i (asid_i), + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_asid_i), // from TLBs // did we miss? .itlb_access_i(itlb_lu_access), @@ -272,6 +274,9 @@ module cva6_mmu import ariane_pkg::*; #( .ptw_error_o ( ptw_error ), .ptw_access_exception_o ( ptw_access_exception ), + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + .lsu_is_store_i(lsu_is_store_i), // PTW memory interface .req_port_i ( req_port_i ), @@ -310,7 +315,7 @@ module cva6_mmu import ariane_pkg::*; #( .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}), @@ -402,16 +407,7 @@ module cva6_mmu import ariane_pkg::*; #( icache_areq_o.fetch_valid = 1'b0; - // // 4K page - // icache_areq_o.fetch_paddr = {mmu_v_st_enbl_i[1] ? itlb_content[1].ppn : itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]}; - // // Mega page - // if (itlb_is_2M) begin - // icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12]; - // end - // // Giga page - // if (itlb_is_1G) begin - // icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12]; - // end + // --------- // ITLB Hit // -------- diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index a4f5cf0e07..553c23c0c2 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -22,7 +22,7 @@ module cva6_ptw import ariane_pkg::*; #( parameter int unsigned HYP_EXT = 0, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, parameter int unsigned VPN_LEN = 1, - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, // Clock @@ -34,7 +34,8 @@ module cva6_ptw import ariane_pkg::*; #( output logic walking_instr_o, // set when walking for TLB output logic [HYP_EXT*2:0] ptw_error_o, // set when an error occurred output logic ptw_access_exception_o, // set when an PMP access exception occured - + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores input logic hlvx_inst_i, // is a HLVX load/store instruction input logic lsu_is_store_i, // this translation was triggered by a store @@ -52,7 +53,7 @@ module cva6_ptw import ariane_pkg::*; #( // from TLBs // did we miss? - input logic [HYP_EXT*2:0] shared_tlb_access_i, + input logic shared_tlb_access_i, input logic shared_tlb_hit_i, input logic [riscv::VLEN-1:0] shared_tlb_vaddr_i, @@ -174,16 +175,17 @@ module cva6_ptw import ariane_pkg::*; #( // update the correct page table level for (int unsigned y=0; y < HYP_EXT+1; y++) begin for (int unsigned x=0; x < PT_LEVELS-1; x++) begin - if(&shared_tlb_access_i[HYP_EXT:0] && HYP_EXT==1) + if((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0])&& HYP_EXT==1) begin shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); - else if(shared_tlb_access_i[0] || HYP_EXT==0) + end else if(enable_translation_i[0] || en_ld_st_translation_i[0] || HYP_EXT==0) begin shared_tlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; - else + end else begin shared_tlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; + end end // set the global mapping bit - if(shared_tlb_access_i[HYP_EXT] && HYP_EXT==1) begin + if((enable_translation_i[HYP_EXT] || en_ld_st_translation_i[HYP_EXT]) && HYP_EXT==1) begin shared_tlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; end else begin shared_tlb_update_o.content[y] = y==0 ? (pte[0] | (global_mapping_q << 5)) : '0; @@ -291,19 +293,19 @@ module cva6_ptw import ariane_pkg::*; #( if(HYP_EXT==1) pte[HYP_EXT*2] = '0; // if we got an ITLB miss - if (|shared_tlb_access_i & ~shared_tlb_hit_i) begin - if (&shared_tlb_access_i[HYP_EXT:0] && HYP_EXT==1) begin + if (((|enable_translation_i[HYP_EXT:0]) || |en_ld_st_translation_i[HYP_EXT:0]) && shared_tlb_access_i && ~shared_tlb_hit_i) begin + if ((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0]) && HYP_EXT==1) begin ptw_stage_d = G_INTERMED_STAGE; pptr = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; gptw_pptr_n = pptr; ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else if (!shared_tlb_access_i[0] && HYP_EXT==1) begin + end else if (((|enable_translation_i[HYP_EXT:0] && !enable_translation_i[0]) || (|en_ld_st_translation_i[HYP_EXT:0] && !en_ld_st_translation_i[0])) && HYP_EXT==1) begin ptw_stage_d = G_FINAL_STAGE; gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; end else begin ptw_stage_d = S_STAGE; - if(shared_tlb_access_i[HYP_EXT*2] && HYP_EXT==1) + if((enable_translation_i[HYP_EXT*2] || en_ld_st_translation_i[HYP_EXT*2]) && HYP_EXT==1) ptw_pptr_n = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; else ptw_pptr_n = {satp_ppn_i[0], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; @@ -315,7 +317,7 @@ module cva6_ptw import ariane_pkg::*; #( shared_tlb_miss_o = 1'b1; for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_n[b] = b==0 ? (shared_tlb_access_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + tlb_update_asid_n[b] = b==0 ? ((enable_translation_i[2*HYP_EXT] || en_ld_st_translation_i[2*HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; end end end @@ -355,7 +357,7 @@ module cva6_ptw import ariane_pkg::*; #( if (pte[0].r || pte[0].x) begin case (ptw_stage_q) S_STAGE: begin - if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT]) begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin state_d = WAIT_GRANT; ptw_stage_d = G_FINAL_STAGE; if(HYP_EXT==1) @@ -390,7 +392,7 @@ module cva6_ptw import ariane_pkg::*; #( if (!pte[0].x || !pte[0].a) begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; - end else if((ptw_stage_q == G_FINAL_STAGE) || !shared_tlb_access_i[HYP_EXT] || HYP_EXT==0) + end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) shared_tlb_update_o.valid = 1'b1; end else begin @@ -402,8 +404,8 @@ module cva6_ptw import ariane_pkg::*; #( // 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && shared_tlb_access_i[HYP_EXT*2] && HYP_EXT==1))))) begin - if((ptw_stage_q == G_FINAL_STAGE) || !shared_tlb_access_i[HYP_EXT] || HYP_EXT==0) + if (pte[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin + if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) shared_tlb_update_o.valid = 1'b1; end else begin state_d = PROPAGATE_ERROR; @@ -427,7 +429,7 @@ module cva6_ptw import ariane_pkg::*; #( end // check if 63:41 are all zeros - if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT*2] && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin + if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin state_d = PROPAGATE_ERROR; ptw_stage_d = G_FINAL_STAGE; end @@ -448,7 +450,7 @@ module cva6_ptw import ariane_pkg::*; #( case (ptw_stage_q) S_STAGE: begin - if (HYP_EXT==1 && shared_tlb_access_i[HYP_EXT]) begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin ptw_stage_d = G_INTERMED_STAGE; if(HYP_EXT==1) pte[HYP_EXT*2] = pte[0]; @@ -477,7 +479,7 @@ module cva6_ptw import ariane_pkg::*; #( end // check if 63:41 are all zeros - if (HYP_EXT==1 && (shared_tlb_access_i[HYP_EXT*2] && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin + if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; end @@ -554,7 +556,7 @@ module cva6_ptw import ariane_pkg::*; #( data_rdata_q <= '0; data_rvalid_q <= 1'b0; if(HYP_EXT==1) - pte[HYP_EXT] = '0; + pte[HYP_EXT] <= '0; end else begin state_q <= state_d; ptw_stage_q <= ptw_stage_d; @@ -571,7 +573,7 @@ module cva6_ptw import ariane_pkg::*; #( data_rvalid_q <= req_port_i.data_rvalid; if(HYP_EXT==1) - pte[HYP_EXT] = pte[HYP_EXT*2]; + pte[HYP_EXT] <= pte[HYP_EXT*2]; end end diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 8ec9463eea..4a354d6f5c 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -144,7 +144,7 @@ always_comb begin : translation for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // first level match, this may be a giga 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) - match_asid[i][0] = (((lu_asid_i[0] == tags_q[i].asid[0]) || content_q[i][0].g) && v_st_enbl_i[0]) || !v_st_enbl_i[0]; + match_asid[i][0] = (((lu_asid_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) || content_q[i][0].g) && v_st_enbl_i[0]) || !v_st_enbl_i[0]; if(HYP_EXT==1) begin match_asid[i][HYP_EXT] = (lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[HYP_EXT]) || !v_st_enbl_i[HYP_EXT]; @@ -222,39 +222,39 @@ always_comb begin : update_flush else if (asid_to_be_flushed_is0[0] && (|vaddr_level_match[i][0] ) && (~vaddr_to_be_flushed_is0[0])) 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][0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0]) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) + else if ((!content_q[i][0].g) && (|vaddr_level_match[i][0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] ) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) 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[0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0]) && (!asid_to_be_flushed_is0[0])) + else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] ) && (!asid_to_be_flushed_is0[0])) tags_n[i].valid = 1'b0; end - end else if (flush_i[HYP_EXT]) begin + end else if (flush_i[HYP_EXT] && HYP_EXT==1) begin if(tags_q[i].v_st_enbl[HYP_EXT*2] && tags_q[i].v_st_enbl[0]) begin // invalidate logic // flush everything if current VMID matches and ASID is 0 and vaddr is 0 ("SFENCE.VMA/HFENCE.VVMA x0 x0" case) - if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[1][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) + if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) tags_n[i].valid = 1'b0; // flush vaddr in all addressing space if current VMID matches ("SFENCE.VMA/HFENCE.VVMA vaddr x0" case), it should happen only for leaf pages else if (asid_to_be_flushed_is0[0] && (|vaddr_level_match[i][0]) && (~vaddr_to_be_flushed_is0[0]) && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[1][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global and asid and vaddr and current VMID matches with the entry to be flushed ("SFENCE.VMA/HFENCE.VVMA vaddr asid" case) - else if ((!content_q[i][0].g) && (|vaddr_level_match[i][0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) + else if ((!content_q[i][0].g) && (|vaddr_level_match[i][0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) tags_n[i].valid = 1'b0; // the entry is flushed if it's not global, and the asid and the current VMID matches and vaddr is 0. ("SFENCE.VMA/HFENCE.VVMA 0 asid" case) - else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0] == tags_q[i].asid[0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!asid_to_be_flushed_is0[0])) + else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!asid_to_be_flushed_is0[0])) tags_n[i].valid = 1'b0; end - end else if (flush_i[HYP_EXT*2]) begin + end else if (flush_i[HYP_EXT*2] && HYP_EXT==1) begin if(tags_q[i].v_st_enbl[HYP_EXT]) begin // invalidate logic // flush everything if vmid is 0 and addr is 0 ("HFENCE.GVMA x0 x0" case) if (asid_to_be_flushed_is0[HYP_EXT] && vaddr_to_be_flushed_is0[HYP_EXT] ) tags_n[i].valid = 1'b0; // flush gpaddr in all addressing space ("HFENCE.GVMA gpaddr x0" case), it should happen only for leaf pages - else if (asid_to_be_flushed_is0[HYP_EXT] && (|vaddr_level_match[i][1] ) && (~vaddr_to_be_flushed_is0[HYP_EXT])) + else if (asid_to_be_flushed_is0[HYP_EXT] && (|vaddr_level_match[i][HYP_EXT] ) && (~vaddr_to_be_flushed_is0[HYP_EXT])) tags_n[i].valid = 1'b0; // the entry vmid and gpaddr both matches with the entry to be flushed ("HFENCE.GVMA gpaddr vmid" case) - else if ((|vaddr_level_match[i][1]) && (asid_to_be_flushed_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) && (~vaddr_to_be_flushed_is0[HYP_EXT]) && (~asid_to_be_flushed_is0[HYP_EXT])) + else if ((|vaddr_level_match[i][HYP_EXT]) && (asid_to_be_flushed_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) && (~vaddr_to_be_flushed_is0[HYP_EXT]) && (~asid_to_be_flushed_is0[HYP_EXT])) tags_n[i].valid = 1'b0; // the entry is flushed if the vmid matches and gpaddr is 0. ("HFENCE.GVMA 0 vmid" case) else if ((vaddr_to_be_flushed_is0[HYP_EXT]) && (asid_to_be_flushed_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) && (!asid_to_be_flushed_is0[HYP_EXT])) From cb563340f5a3b538e12cb19baa5a6f80c040a248 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Wed, 14 Feb 2024 18:04:02 +0100 Subject: [PATCH 083/182] Common shared TLB - sv32 and sv39 ok. sv39x4 fails --- core/mmu_unify/cva6_shared_tlb.sv | 119 ++++++++++++++++-------------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 002314bc1b..283430a627 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -23,7 +23,6 @@ /* verilator lint_off WIDTH */ module cva6_shared_tlb -import ariane_pkg::*; #( parameter type pte_cva6_t = logic, parameter type tlb_update_cva6_t = logic, @@ -37,13 +36,14 @@ import ariane_pkg::*; ) ( 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 [HYP_EXT*2:0] flush_i, // Flush signal [g_stage,vs stage, normal translation signal] + input logic [1:0][HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] dtlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] itlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] // from TLBs // did we miss? @@ -63,7 +63,7 @@ import ariane_pkg::*; output logic itlb_miss_o, output logic dtlb_miss_o, - output logic [HYP_EXT*2:0] shared_tlb_access_o, + output logic shared_tlb_access_o, output logic shared_tlb_hit_o, output logic [riscv::VLEN-1:0] shared_tlb_vaddr_o, @@ -110,17 +110,17 @@ 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(pte_cva6_t)-1:0] pte_wr_data; +logic [$bits(pte_cva6_t)-1:0] pte_wr_data [HYP_EXT:0]; 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][HYP_EXT: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+HYP_EXT-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; @@ -135,13 +135,15 @@ logic [riscv::VLEN-1-12:0] dtlb_vpn_q; logic [ASID_WIDTH[0]-1:0] tlb_update_asid_q [HYP_EXT:0], tlb_update_asid_d[HYP_EXT:0]; -logic [HYP_EXT*2:0] shared_tlb_access_q, shared_tlb_access_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; +int i_req; + // replacement strategy logic [SHARED_TLB_WAYS-1:0] way_valid; logic update_lfsr; // shift the LFSR @@ -157,6 +159,8 @@ assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; assign itlb_req_o = itlb_req_q; +assign i_req = itlb_req_q ? 1 : 0; + genvar i,x; generate for (i=0; i < SHARED_TLB_WAYS; i++) begin @@ -164,15 +168,15 @@ genvar i,x; for (x=0; x < PT_LEVELS; x++) begin assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[HYP_EXT:0])): - ((&v_st_enbl_i[HYP_EXT:0]) ? + &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[i_req][HYP_EXT:0])): + ((&v_st_enbl_i[i_req][HYP_EXT:0]) ? ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) - || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): - shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[HYP_EXT])); + || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): + shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[i_req][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[i_req][HYP_EXT])); //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[0]) ? // - vpn_q[x] == shared_tag_rd[i].vpn[x] && shared_tag_rd[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[i_req][0]) ? // + vpn_q[x] == shared_tag_rd[i].vpn[x] && vpn_q[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // vpn_q[x] == shared_tag_rd[i].vpn[x]; //identify if there is a hit at each PT level for all TLB entries @@ -229,13 +233,9 @@ genvar w; itlb_miss_o = 1'b1; itlb_req_d = 1'b1; - // tlb_update_asid_d = asid_i; - - for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_d[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; - end + tlb_update_asid_d = itlb_asid_i; - shared_tlb_access_d = v_st_enbl_i; + shared_tlb_access_d = '1; shared_tlb_vaddr_d = itlb_vaddr_i; // we got an DTLB miss @@ -247,17 +247,13 @@ genvar w; dtlb_miss_o = 1'b1; dtlb_req_d = 1'b1; + tlb_update_asid_d = dtlb_asid_i; - for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_d[b] = b==0 ? (en_ld_st_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; - end - - shared_tlb_access_d = v_st_enbl_i; + shared_tlb_access_d = '1; 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; @@ -266,14 +262,14 @@ genvar w; for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin // first level match, this may be a giga 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) - match_asid[i][0] = (((tlb_update_asid_q[0] == shared_tag_rd[i].asid[0]) || pte[i][0].g) && v_st_enbl_i[0]) || !v_st_enbl_i[0]; + match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req][0]) || !v_st_enbl_i[i_req][0]; if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[HYP_EXT]) || !v_st_enbl_i[HYP_EXT]; + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req][HYP_EXT]) || !v_st_enbl_i[i_req][HYP_EXT]; end // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i; + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req]; if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin if (|level_match[i]) begin @@ -283,24 +279,24 @@ genvar w; 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[0] = pte[i][0]; + itlb_update_o.content = pte[i]; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin itlb_update_o.asid[a] = tlb_update_asid_q[a]; - end + end 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[0] = pte[i][0]; + dtlb_update_o.content = pte[i]; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin dtlb_update_o.asid[a] = tlb_update_asid_q[a]; end end end end - end //tag_comparison - end + end + end //tag_comparison // sequential process always_ff @(posedge clk_i or negedge rst_ni) begin @@ -333,7 +329,7 @@ genvar w; // Update and Flush // ------------------ always_comb begin : update_flush - shared_tag_valid_d = shared_tag_valid_q; + shared_tag_valid_d =shared_tag_valid_q; tag_wr_en = '0; pte_wr_en = '0; @@ -345,6 +341,7 @@ genvar w; 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; + shared_tag_wr.v_st_enbl = v_st_enbl_i[i_req]; end end end @@ -352,13 +349,16 @@ genvar w; assign shared_tag_wr.asid = shared_tlb_update_i.asid; assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; - assign shared_tag_wr.v_st_enbl = v_st_enbl_i; 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 + if(HYP_EXT==1) begin + //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 + assign shared_tag_wr.vpn[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = shared_tlb_update_i.vpn[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)]; + end endgenerate @@ -366,7 +366,14 @@ genvar w; 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]; + + genvar h; + generate + for (h=0; h < HYP_EXT+1; h++) begin + assign pte_wr_data[h] = shared_tlb_update_i.content[h]; + end + endgenerate + 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; @@ -423,23 +430,25 @@ genvar w; assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]); - // PTE RAM - sram #( - .DATA_WIDTH($bits(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][0] = pte_cva6_t'(pte_rd_data[i]); + for (genvar a = 0; a < HYP_EXT+1; a++) begin : content_sram + // PTE RAM + sram #( + .DATA_WIDTH($bits(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[a]), + .be_i ('1), + .ruser_o(), + .rdata_o(pte_rd_data[i][a]) + ); + assign pte[i][a] = pte_cva6_t'(pte_rd_data[i][a]); + end end endmodule From c426c2d6fa15de0fda598283dd3f86d13e5b43b5 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Thu, 15 Feb 2024 10:28:31 +0100 Subject: [PATCH 084/182] Polish commonalization with sv39x4 - changes required to use this MMU in the hypervisor repo are written in comments in the top MMU file. Right now sv39x4 does not boot with the shared TLB --- core/mmu_unify/cva6_mmu.sv | 45 +++++++++++++++++-------------- core/mmu_unify/cva6_shared_tlb.sv | 15 +++++------ core/mmu_unify/cva6_tlb.sv | 21 ++++++++------- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 23447eeb53..d4781f94ec 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -9,17 +9,18 @@ // specific language governing permissions and limitations under the License. // // Author: Angela Gonzalez, PlanV Technology -// Date: 26/01/2024 +// Date: 14/02/2024 // // Description: Memory Management Unit for CVA6, contains TLB and -// address translation unit. SV32 and SV39 as defined in RISC-V +// address translation unit. SV32 SV39 and SV39x4 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. +// by Florian Zaruba, the MMU Sv32 developed by Sebastien Jacq and the MMU Sv39x4 developed by Bruno Sá. module cva6_mmu import ariane_pkg::*; #( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now parameter int unsigned INSTR_TLB_ENTRIES = 4, parameter int unsigned DATA_TLB_ENTRIES = 4, parameter logic HYP_EXT = 0, @@ -37,6 +38,9 @@ module cva6_mmu import ariane_pkg::*; #( // IF interface input icache_arsp_t icache_areq_i, output icache_areq_t icache_areq_o, + // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now + // output icache_areq_i_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 @@ -145,7 +149,7 @@ module cva6_mmu import ariane_pkg::*; #( logic dtlb_lu_hit; logic [riscv::GPLEN-1:0] dtlb_gpaddr; - logic [HYP_EXT*2:0] shared_tlb_access; + logic shared_tlb_access; logic shared_tlb_hit, itlb_req; // Assignments @@ -167,8 +171,7 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( flush_tlb_i ), - // .v_st_enbl_i ( enable_translation_i ), - .v_st_enbl_i('1), + .v_st_enbl_i ( enable_translation_i ), .update_i ( update_itlb ), .lu_access_i ( itlb_lu_access ), .lu_asid_i ( itlb_mmu_asid_i ), @@ -194,8 +197,7 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( flush_tlb_i ), - // .v_st_enbl_i ( en_ld_st_translation_i ), - .v_st_enbl_i('1), + .v_st_enbl_i ( en_ld_st_translation_i ), .update_i ( update_dtlb ), .lu_access_i ( dtlb_lu_access ), .lu_asid_i ( dtlb_mmu_asid_i ), @@ -223,9 +225,7 @@ module cva6_mmu import ariane_pkg::*; #( .clk_i (clk_i), .rst_ni (rst_ni), .flush_i(flush_tlb_i), - .v_st_enbl_i({'1,'1}), - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), + .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), .dtlb_asid_i (dtlb_mmu_asid_i), .itlb_asid_i (itlb_mmu_asid_i), @@ -258,6 +258,7 @@ module cva6_mmu import ariane_pkg::*; #( cva6_ptw #( .CVA6Cfg (CVA6Cfg), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now .pte_cva6_t(pte_cva6_t), .tlb_update_cva6_t(tlb_update_cva6_t), .HYP_EXT(HYP_EXT), @@ -371,11 +372,11 @@ module cva6_mmu import ariane_pkg::*; #( // 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[0] = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); if(HYP_EXT==1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && !itlb_content[HYP_EXT].u; + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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 @@ -518,7 +519,7 @@ module cva6_mmu import ariane_pkg::*; #( 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[HYP_EXT:0]) && !pmp_instr_allow)) begin + if ((!match_any_execute_region && (!ptw_error[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -539,13 +540,15 @@ module cva6_mmu import ariane_pkg::*; #( // 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}); + // assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now // Instruction fetch pmp #( - .CVA6Cfg (CVA6Cfg), + .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension .PLEN ( riscv::PLEN ), .PMP_LEN ( riscv::PLEN - 2 ), .NR_ENTRIES ( CVA6Cfg.NrPMPEntries ) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension ) i_pmp_if ( .addr_i ( icache_areq_o.fetch_paddr ), .priv_lvl_i, @@ -645,11 +648,12 @@ module cva6_mmu import ariane_pkg::*; #( // Check if the User flag is set, then we may only access it in supervisor mode // if SUM is enabled - daccess_err[0] = ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].u) || // SUM is not set and we are trying to access a user page in supervisor mode + daccess_err[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); if(HYP_EXT==1) - daccess_err[HYP_EXT] = !dtlb_pte_q[1].u; + daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; // translation is enabled and no misaligned exception occurred if ((|en_ld_st_translation_i[HYP_EXT:0]) && !misaligned_ex_q.valid) begin lsu_valid_o = 1'b0; @@ -668,7 +672,7 @@ module cva6_mmu import ariane_pkg::*; #( 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(HYP_EXT==1 && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + if(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, @@ -677,7 +681,7 @@ module cva6_mmu import ariane_pkg::*; #( en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else if ((!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, @@ -891,10 +895,11 @@ module cva6_mmu import ariane_pkg::*; #( // Load/store PMP check pmp #( - .CVA6Cfg (CVA6Cfg), + .CVA6Cfg (CVA6Cfg), // COMMENT IN HYPERVISOR EXTENSION .PLEN ( riscv::PLEN ), .PMP_LEN ( riscv::PLEN - 2 ), .NR_ENTRIES ( CVA6Cfg.NrPMPEntries ) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION ) i_pmp_data ( .addr_i ( lsu_paddr_o ), .priv_lvl_i ( ld_st_priv_lvl_i ), diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 283430a627..fe64deb9de 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -38,9 +38,6 @@ module cva6_shared_tlb 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 [1:0][HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - input logic [ASID_WIDTH[0]-1:0] dtlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] input logic [ASID_WIDTH[0]-1:0] itlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] @@ -189,18 +186,18 @@ endgenerate genvar w; generate for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (enable_translation_i[0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + assign vpn_d[w] = (v_st_enbl_i[1][0] & 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[0] & dtlb_access_i & ~dtlb_hit_i)? // + ((v_st_enbl_i[0][0] & 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 if(HYP_EXT==1) //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = (enable_translation_i[0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = (v_st_enbl_i[1][0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // itlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : // - ((en_ld_st_translation_i[0] & dtlb_access_i & ~dtlb_hit_i)? // + ((v_st_enbl_i[0][0] & dtlb_access_i & ~dtlb_hit_i)? // dtlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : vpn_q[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]); /////////////////////////////////////////////////////// @@ -225,7 +222,7 @@ genvar w; pte_rd_addr = '0; // if we got an ITLB miss - if ((|enable_translation_i[HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin + if ((|v_st_enbl_i[1][HYP_EXT:0]) & 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; @@ -239,7 +236,7 @@ genvar w; shared_tlb_vaddr_d = itlb_vaddr_i; // we got an DTLB miss - end else if ((|en_ld_st_translation_i[HYP_EXT:0]) & dtlb_access_i & ~dtlb_hit_i) begin + end else if ((|v_st_enbl_i[0][HYP_EXT:0]) & 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; diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index 4a354d6f5c..def47577b9 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -73,6 +73,9 @@ logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vpage_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; logic [TLB_ENTRIES-1:0] match_stage; pte_cva6_t g_content; +logic [HYP_EXT*2:0] v_st_enbl; + +assign v_st_enbl = (HYP_EXT==1) ? v_st_enbl_i : '1; //------------- // Translation //------------- @@ -83,14 +86,14 @@ for (i=0; i < TLB_ENTRIES; i++) begin 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==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[HYP_EXT:0])): - ((&v_st_enbl_i[HYP_EXT:0]) ? + &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl[HYP_EXT:0])): + ((&v_st_enbl[HYP_EXT:0]) ? ((tags_q[i].is_page[PT_LEVELS-1-x][0] && (tags_q[i].is_page[PT_LEVELS-2-x][HYP_EXT] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT])) || (tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): - tags_q[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[0] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[HYP_EXT])); + tags_q[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl[0] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl[HYP_EXT])); //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[0]) ? // + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl[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[12+VPN_LEN-1: 12+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]; @@ -144,19 +147,19 @@ always_comb begin : translation for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin // first level match, this may be a giga 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) - match_asid[i][0] = (((lu_asid_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) || content_q[i][0].g) && v_st_enbl_i[0]) || !v_st_enbl_i[0]; + match_asid[i][0] = (((lu_asid_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0]) || content_q[i][0].g) && v_st_enbl[0]) || !v_st_enbl[0]; if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[HYP_EXT]) || !v_st_enbl_i[HYP_EXT]; + match_asid[i][HYP_EXT] = (lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl[HYP_EXT]) || !v_st_enbl[HYP_EXT]; end // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = tags_q[i].v_st_enbl == v_st_enbl_i; + match_stage[i] = tags_q[i].v_st_enbl == v_st_enbl; if (tags_q[i].valid && &match_asid[i] && match_stage[i]) begin if(HYP_EXT==1 && vpn_match[i][HYP_EXT*2]) - lu_gpaddr_o = make_gpaddr(v_st_enbl_i[0], tags_q[i].is_page[0][0], tags_q[i].is_page[1][0], lu_vaddr_i, content_q[i][0]); + lu_gpaddr_o = make_gpaddr(v_st_enbl[0], tags_q[i].is_page[0][0], tags_q[i].is_page[1][0], lu_vaddr_i, content_q[i][0]); if (|level_match[i]) begin lu_is_page_o = is_page_o[i]; @@ -264,7 +267,7 @@ always_comb begin : update_flush end else if (update_i.valid & replace_en[i]) begin // update tag array tags_n[i].asid = update_i.asid; - tags_n[i].v_st_enbl= v_st_enbl_i; + tags_n[i].v_st_enbl= v_st_enbl; tags_n[i].is_page= update_i.is_page; tags_n[i].valid= 1'b1; From 52c05d4d4a0d91f09a3b53396932a932feba70b3 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 16 Feb 2024 08:49:35 +0100 Subject: [PATCH 085/182] add parameter to bypass shared TLB --- core/mmu_unify/cva6_mmu.sv | 1 + core/mmu_unify/cva6_shared_tlb.sv | 117 +++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 35 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d4781f94ec..38dbf519e3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -215,6 +215,7 @@ module cva6_mmu import ariane_pkg::*; #( .SHARED_TLB_DEPTH (64), .SHARED_TLB_WAYS (2), .HYP_EXT(HYP_EXT), + .BYPASS(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index fe64deb9de..98da55efe6 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -29,6 +29,7 @@ module cva6_shared_tlb parameter int SHARED_TLB_DEPTH = 64, parameter int SHARED_TLB_WAYS = 2, parameter int unsigned HYP_EXT = 0, + parameter int unsigned BYPASS = 0, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, @@ -71,6 +72,9 @@ module cva6_shared_tlb ); +tlb_update_cva6_t shared_tlb_update_delayed,shared_tlb_update_delayed2; +logic shared_tlb_update_valid_delayed,shared_tlb_update_valid_delayed2; + 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; @@ -255,44 +259,66 @@ genvar w; 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 - // first level match, this may be a giga 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) - match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req][0]) || !v_st_enbl_i[i_req][0]; - - if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req][HYP_EXT]) || !v_st_enbl_i[i_req][HYP_EXT]; - end - - // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req]; - - if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin - if (|level_match[i]) 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]; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - itlb_update_o.asid[a] = tlb_update_asid_q[a]; - end - 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]; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - dtlb_update_o.asid[a] = tlb_update_asid_q[a]; + + if(BYPASS==1) begin + if(shared_tlb_update_valid_delayed2) begin + shared_tlb_hit_d = 1'b1; + if (itlb_req_q) begin + itlb_update_o.valid = 1'b1; + itlb_update_o.vpn = shared_tlb_update_delayed2.vpn; + itlb_update_o.is_page = shared_tlb_update_delayed2.is_page; + itlb_update_o.content = shared_tlb_update_delayed2.content; + itlb_update_o.asid = shared_tlb_update_delayed2.asid; + + end else if (dtlb_req_q) begin + dtlb_update_o.valid = 1'b1; + dtlb_update_o.vpn = shared_tlb_update_delayed2.vpn; + dtlb_update_o.is_page = shared_tlb_update_delayed2.is_page; + dtlb_update_o.content = shared_tlb_update_delayed2.content; + dtlb_update_o.asid = shared_tlb_update_delayed2.asid; + end + end + end + else begin + //number of ways + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + // first level match, this may be a giga 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) + match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req][0]) || !v_st_enbl_i[i_req][0]; + + if(HYP_EXT==1) begin + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req][HYP_EXT]) || !v_st_enbl_i[i_req][HYP_EXT]; + end + + // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req]; + + if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin + if (|level_match[i]) 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]; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + itlb_update_o.asid[a] = tlb_update_asid_q[a]; + end + 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]; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + dtlb_update_o.asid[a] = tlb_update_asid_q[a]; + end end end end - end - end + end + end end //tag_comparison // sequential process @@ -308,6 +334,10 @@ genvar w; itlb_req_q <= '0; dtlb_req_q <= '0; shared_tag_valid <= '0; + shared_tlb_update_valid_delayed<='0; + shared_tlb_update_valid_delayed2<='0; + shared_tlb_update_delayed<='0; + shared_tlb_update_delayed2<='0; end else begin itlb_vpn_q <= itlb_vaddr_i[riscv::SV-1:12]; dtlb_vpn_q <= dtlb_vaddr_i[riscv::SV-1:12]; @@ -319,6 +349,23 @@ genvar w; itlb_req_q <= itlb_req_d; dtlb_req_q <= dtlb_req_d; shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; + + if(shared_tlb_update_i.valid) begin + shared_tlb_update_valid_delayed <=shared_tlb_update_i.valid; + shared_tlb_update_delayed <= shared_tlb_update_i; + end + + + shared_tlb_update_valid_delayed2 <= shared_tlb_update_valid_delayed; + shared_tlb_update_delayed2 <= shared_tlb_update_delayed; + + if(|flush_i) begin + shared_tlb_update_valid_delayed<='0; + shared_tlb_update_valid_delayed2<='0; + shared_tlb_update_delayed<='0; + shared_tlb_update_delayed2<='0; + end + end end From b7a06955dfd6fd08a6e954cd110404816b99c3e9 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 16 Feb 2024 09:41:03 +0100 Subject: [PATCH 086/182] Documentation v0 --- docs/01_cva6_user/Programmer_View.rst | 10 +- .../source/cv32a6_execute.rst | 306 +++++++++++------- 2 files changed, 189 insertions(+), 127 deletions(-) diff --git a/docs/01_cva6_user/Programmer_View.rst b/docs/01_cva6_user/Programmer_View.rst index e54d152272..ba28e09ff5 100644 --- a/docs/01_cva6_user/Programmer_View.rst +++ b/docs/01_cva6_user/Programmer_View.rst @@ -61,10 +61,14 @@ Note: The addition of the H Extension is in the process. After that, HS, VS, and RISC-V Virtual Memory --------------------- -CV32A6 supports the RISC-V **Sv32** virtual memory when the ``MMUEn`` parameter is set to 1 (and ``Xlen`` is set to 32). +CV32A6 supports the RISC-V **Sv32** virtual memory when the ``MMUEn`` parameter is set to 1 (and ``Xlen`` is set to 32). CV64A6 supports the RISC-V **Sv39** virtual memory when the ``MMUEn`` parameter is set to 1 (and ``Xlen`` is set to 64). +Within CV64A6, the hypervisor extension is enabled and supports **Sv39x4** virtual memory when the ``CVA6ConfigHExtEn`` parameter is set to 1 (and ``Xlen`` is set to 64). + +**PENDING: WHAT IS THE RIGHT PART NUMBER FOR THE HYPERVISOR EXTENSION? HOW IS IT ENABLED VIA SW?** + By default, CV32A6 and CV64A6 are in RISC-V **Bare** mode. **Sv32** or **Sv39** are enabled by writing 1 to ``satp[0]`` register bit. When the ``MMUEn`` parameter is set to 0, CV32A6 and CV64A6 are always in RISC-V **Bare** mode; ``satp[0]`` remains at 0 and writes to this register are ignored. @@ -73,9 +77,7 @@ Notes for the integrator: * The virtual memory is implemented by a memory management unit (MMU) that accelerates the translation from virtual memory addresses (as handled by the core) to physical memory addresses. The MMU integrates translation lookaside buffers (TLB) and a hardware page table walker (PTW). The number of instruction and data TLB entries are configured with ``InstrTlbEntries`` and ``DataTlbEntries``. -* The CV32A6 MMU will evolve with a microarchitectural optimization featuring two levels of TLB: level 1 TBL (sized by ``InstrTlbEntries`` and ``DataTlbEntries``) and a shared level 2 TLB. This optimization remains to be implemented in CV64A6. The optimization has no consequences on the programmer's view. - -* The addition of the hypervisor support will come with **Sv39x4** virtual memory that is not yet documented here. +* The MMU is implemented with a microarchitectural optimization featuring two levels of TLB: level 1 TBL (sized by ``InstrTlbEntries`` and ``DataTlbEntries``) and a shared level 2 TLB. The optimization has no consequences on the programmer's view. Memory Alignment ---------------- diff --git a/docs/04_cv32a6_design/source/cv32a6_execute.rst b/docs/04_cv32a6_design/source/cv32a6_execute.rst index 092cb3257b..ea76f58485 100644 --- a/docs/04_cv32a6_design/source/cv32a6_execute.rst +++ b/docs/04_cv32a6_design/source/cv32a6_execute.rst @@ -29,7 +29,7 @@ Load Store Unit (LSU) Memory Management Unit ---------------------- -The Memory Management Unit (MMU) SV32 module is a crucial component in the RISC-V-based processor, serving as the backbone for virtual memory management and address translation. +The Memory Management Unit (MMU) module is a crucial component in the RISC-V-based processor, serving as the backbone for virtual memory management and address translation. The MMU block can be parameterized to support sv32, sv39 and sv39x4 virtual memory. .. figure:: ../images/mmu_in_out.png :name: **Figure 1:** Inputs and Outputs of CVA6 MMU SV32 @@ -39,7 +39,7 @@ The Memory Management Unit (MMU) SV32 module is a crucial component in the RISC- **Figure 1:** Inputs and Outputs of CVA6 MMU SV32 -At its core, the MMU SV32 plays a pivotal role in translating virtual addresses into their corresponding physical counterparts. This translation process is paramount for providing memory protection, isolation, and efficient memory management in modern computer systems. Importantly, it handles both instruction and data accesses, ensuring a seamless interaction between the processor and virtual memory. Within the MMU, several major blocks play pivotal roles in this address translation process. These includes: +At its core, the MMU plays a pivotal role in translating virtual addresses into their corresponding physical counterparts. This translation process is paramount for providing memory protection, isolation, and efficient memory management in modern computer systems. Importantly, it handles both instruction and data accesses, ensuring a seamless interaction between the processor and virtual memory. Within the MMU, several major blocks play pivotal roles in this address translation process. These includes: * Instruction TLB (ITLB) * Data TLB (DTLB) @@ -47,22 +47,22 @@ At its core, the MMU SV32 plays a pivotal role in translating virtual addresses * Page Table Walker (PTW) .. figure:: ../images/mmu_major_blocks.png - :name: **Figure 2:** Major Blocks in CVA6 MMU SV32 + :name: **Figure 2:** Major Blocks in CVA6 MMU :align: center :width: 60% :alt: mmu_major_blocks - **Figure 2:** Major Blocks in CVA6 MMU SV32 + **Figure 2:** Major Blocks in CVA6 MMU -The MMU SV32 manages privilege levels and access control, enforcing permissions for user and supervisor modes while handling access exceptions. It employs Translation Lookaside Buffers (TLBs) for efficient address translation, reducing the need for page table access. TLB hits yield quick translations, but on misses, the shared TLB is consulted, and if necessary, the Page Table Walker (PTW) performs page table walks, updating TLBs and managing exceptions during the process. +The MMU manages privilege levels and access control, enforcing permissions for user and supervisor modes while handling access exceptions. It employs Translation Lookaside Buffers (TLBs) for efficient address translation, reducing the need for page table access. TLB hits yield quick translations, but on misses, the shared TLB is consulted, and if necessary, the Page Table Walker (PTW) performs page table walks, updating TLBs and managing exceptions during the process. -In addition to these functionalities, the MMU SV32 seamlessly integrates support for Physical Memory Protection (PMP), enabling it to enforce access permissions and memory protection configurations as specified by the PMP settings. This additional layer of security and control enhances the management of memory accesses +In addition to these functionalities, the MMU seamlessly integrates support for Physical Memory Protection (PMP), enabling it to enforce access permissions and memory protection configurations as specified by the PMP settings. This additional layer of security and control enhances the management of memory accesses .. raw:: html Instruction and Data Interfaces -The MMU SV32 maintains interfaces with the instruction cache (ICache) and the load-store unit (LSU). It receives virtual addresses from these components and proceeds to translate them into physical addresses, a fundamental task for ensuring proper program execution and memory access. +The MMU maintains interfaces with the instruction cache (ICache) and the load-store unit (LSU). It receives virtual addresses from these components and proceeds to translate them into physical addresses, a fundamental task for ensuring proper program execution and memory access. .. raw:: html @@ -70,7 +70,7 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo .. raw:: html -

Table 1: CVA6 MMU SV32 Input Output Signals

+

Table 1: CVA6 MMU Input Output Signals

.. list-table:: :header-rows: 1 @@ -102,14 +102,14 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo * - ``enable_translation_i`` - in - CSR RegFile - - logic - - Indicate address translation request for instruction + - logic [HYP_EXT*2:0] + - Bit 0 indicates address translation request for instruction. In Hypervisor mode, bit 1 enables virtual memory translation for instrucionts, and bit 2 indicates the virtualization mode state * - ``en_ld_st_translation_i`` - in - CSR RegFile - logic - - Indicate address translation request for load or store + - Bit 0 indicates address translation request for load or store. In Hypervisor mode, bit 1 enables virtual memory translation for load or store, and bit 2 indicates the virtualization mode at which load and stores should happen * - ``icache_areq_i`` - in @@ -141,12 +141,24 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo - logic [riscv::VLEN-1:0] - Virtual Address In + * - ``lsu_tinst_i`` + - in + - Load Store Unit + - riscv::xlen_t + - Transformed Instruction In when Hypervisor Extension is enabled. Set to 0 (unused) when not. + * - ``lsu_is_store_i`` - in - Store Unit - logic - Translation is requested by a store + * - ``csr_hs_ld_st_inst_o`` + - out + - CSR RegFile + - logic + - Indicate a hypervisor load store instruction. + * - ``lsu_dtlb_hit_o`` - out - Store / Load Unit @@ -192,44 +204,56 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo * - ``sum_i`` - in - CSR RegFile - - logic - - Supervisor User Memory Access bit in xSTATUS CSR register + - logic [HYP_EXT:0] + - Bit 0 is the Supervisor User Memory Access bit in xSTATUS CSR register. Bit 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. * - ``mxr_i`` - in - CSR RegFile - - logic - - Make Executable Readable bit in xSTATUS CSR register + - logic [HYP_EXT:0] + - Bit 0 is the Make Executable Readable bit in xSTATUS CSR register. Bit 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. + + * - ``hlvx_inst_i`` + - in + - Store / Load Unit + - logic [HYP_EXT:0] + - Indicates that Instruction is a hypervisor load store with execute permissions + + * - ``hs_ld_st_inst_i`` + - in + - CSR RegFile + - logic [HYP_EXT:0] + - Indicates that Instruction is a hypervisor load store instruction * - ``satp_ppn_I`` - in - CSR RegFile - - logic [riscv::PPNW-1:0] - - PPN of top level page table from SATP register + - logic [HYP_EXT*2:0][riscv::PPNW-1:0] + - Vector 0 is the PPN of top level page table from SATP register. Vectors 1 and 2 are the analogous one for virtual supervisor and hypervisor when Hypervisor extension is enabled. * - ``asid_i`` - in - CSR RegFile - - logic [ASID_WIDTH-1:0] - - ASID to for the lookup + - logic [HYP_EXT*2:0][ASID_WIDTH-1:0] + - Vector 0 is the ASID for the lookup. Vectors 1 and 2 are the analogous one for virtual supervisor and hypervisor when Hypervisor extension is enabled. - * - ``asid_to_be_flushed`` + * - ``asid_to_be_flushed_i`` - in - Execute Stage - - logic [ASID_WIDTH-1:0] - - ASID of the entry to be flushed. + - logic [HYP_EXT:0][ASID_WIDTH-1:0] + - Vector 0 is the ASID of the entry to be flushed. Vector 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. * - ``vaddr_to_be_flushed_i`` - in - Execute Stage - - logic [riscv::VLEN-1:0] - - Virtual address of the entry to be flushed. + - logic [HYP_EXT:0][riscv::VLEN-1:0] + - Vector 0 is the Virtual address of the entry to be flushed. Vector 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. * - ``flush_tlb_i`` - in - Controller - - logic - - SFENCE.VMA committed + - logic [HYP_EXT*2:0] + - Bit 0 indicates SFENCE.VMA committed. When Hypervisor extension is enabled, bits 1 and 2 respectively indicate SFENCE.VVMA and SFENCE.GVMA committed. * - ``itlb_miss_o`` - out @@ -332,6 +356,18 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo - riscv::xlen_t - Additional information of causing exception (e.g. instruction causing it), address of LD/ST fault + * - ``tval2`` + - logic [riscv::GPLEN-1:0] + - Additional information when the causing exception in a guest exception (used only in hypervisor mode) + + * - ``tinst`` + - riscv::xlen_t + - Transformed instruction information + + * - ``gva`` + - logic + - Signals when a guest virtual address is written to tval + * - ``valid`` - logic - Indicate that exception is valid @@ -365,15 +401,15 @@ The MMU SV32 maintains interfaces with the instruction cache (ICache) and the lo .. raw:: html - Control Flow in MMU SV32 Module + Control Flow in MMU Module .. figure:: ../images/mmu_control_flow.png - :name: **Figure 3:** Control Flow in CVA6 MMU SV32 + :name: **Figure 3:** Control Flow in CVA6 MMU :align: center :width: 95% :alt: mmu_control_flow - **Figure 3:** Control Flow in CVA6 MMU SV32 + **Figure 3:** Control Flow in CVA6 MMU .. raw:: html @@ -397,8 +433,8 @@ The IF stage initiates a request to retrieve memory content at a specific virtua If virtual memory translation is enabled for instruction fetches, the following operations are performed in the instruction interface: * Compatibility of requested virtual address with selected page based address translation scheme is checked. -* For 4K page translation, the module determines the fetch physical address by combining the physical page number (PPN) from ITLB content and the offset from the virtual address. -* In the case of Mega page translation, if the ITLB indicates a 4M page, the VPN0 from the fetch virtual address is written to the PPN0 of the fetch physical address to ensure alignment for superpage translation. +* For page translation, the module determines the fetch physical address by combining the physical page number (PPN) from ITLB content and the offset from the virtual address. +* Depending on the size of the identified page the PPN of the fetch physical address is updated with the corresponding bits of the VPN to ensure alignment for superpage translation. * If the Instruction TLB (ITLB) lookup hits, the fetch valid signal (which indicates a valid physical address) is activated in response to the input fetch request. Memory region accessibility is checked from the perspective of the fetch operation, potentially triggering a page fault exception in case of an access error or insufficient PMP permission. * In case of an ITLB miss, if the page table walker (PTW) is active (only active if there is a shared TLB miss) and handling instruction fetches, the fetch valid signal is determined based on PTW errors or access exceptions. @@ -416,7 +452,7 @@ If address translation is enabled for load or store, and no misaligned exception * Initially, translation is assumed to be invalid, signified by the MMU to LSU. * The translated physical address is formed by combining the PPN from the Page Table Entry (PTE) and the offset from the virtual address requiring translation. This send one cycle later due to the additional bank of registers which delayed the MMU’s answer. The PPN from the PTE is also shared separately with LSU in the same cycle as the hit. -* In the case of superpage translation, as in SV32, known as the 4M page, PPN0 of the translated physical address and the separately shared PPN are updated with the VPN0 of the virtual address. +* In the case of superpage translation, the PPN of the translated physical address and the separately shared PPN are updated with the VPN of the virtual address. If a Data TLB (DTLB) hit occurs, it indicates a valid translation, and various fault checks are performed depending on whether it's a load or store request. @@ -481,13 +517,20 @@ The inputs and output signals of the TLB are shown in the following two figures. * - ``flush_i`` - in - Controller - - logic - - Asynchronous reset active low + - logic [HYP_EXT*2:0] + - Sfence Committed + + * - ``v_st_enbl_i`` + - in + - Controller + - logic [HYP_EXT*2:0] + - Used only in Hypervisor mode. Bit 0 indicates address translation request (s-stage), bit 1 enables virtual memory translation (g-stage), and bit 2 indicates the virtualization mode state + * - ``update_i`` - in - Shared TLB - - tlb_update_sv32_t + - tlb_update_cva6_t - Updated tag and content of TLB * - ``lu_access_i`` @@ -498,9 +541,9 @@ The inputs and output signals of the TLB are shown in the following two figures. * - ``lu_asid_i`` - in - - CSR RegFile - - logic[ASID_WIDTH-1:0] - - ASID (Address Space Identifier) for the lookup + - CVA6 MMU + - logic[ASID_WIDTH[0]-1:0] + - Vector 0 is the ASID (Address Space Identifier) for the lookup. Vector 1 is the analogous one for virtual supervisor or hypervisor when Hypervisor extension is enabled. * - ``lu_vaddr_i`` - in @@ -510,31 +553,31 @@ The inputs and output signals of the TLB are shown in the following two figures. * - ``lu_content_o`` - out - - MMU SV32 - - riscv::pte_sv32_t + - CVA6 MMU + - pte_cva6_t [HYP_EXT:0] - Output for the content of the TLB entry * - ``asid_to_be_flushed_i`` - in - Execute Stage - - logic[ASID_WIDTH-1:0] - - ASID of the entry to be flushed + - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] + - Vector 0 is the ASID of the entry to be flushed. Vector 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. - * - ``vaddr_to_be_flushed_i`` + * - ``vaddr_to_be_flushed_i`` - in - Execute Stage - - logic[riscv::VLEN-1:0] - - Virtual address of the entry to be flushed + - logic [HYP_EXT:0][riscv::VLEN-1:0] + - Vector 0 is the Virtual address of the entry to be flushed. Vector 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. - * - ``lu_is_4M_o`` + * - ``lu_is_page_o`` - out - - MMU SV32 - - logic - - Output indicating whether the TLB entry corresponds to a 4MB page + - CVA6 MMU + - logic [PT_LEVELS-2:0] + - Output indicating whether the TLB entry corresponds to any page at the different levels * - ``lu_hit_o`` - out - - MMU SV32 + - CVA6 MMU - logic - Output indicating whether the lookup resulted in a hit or miss @@ -544,7 +587,7 @@ The inputs and output signals of the TLB are shown in the following two figures. .. raw:: html -

Table 7: SV32 TLB Update Struct (tlb_update_sv32_t)

+

Table 7: SV32 TLB Update Struct (tlb_update_cva6_t)

.. list-table:: :header-rows: 1 @@ -557,25 +600,25 @@ The inputs and output signals of the TLB are shown in the following two figures. - logic - Indicates whether the TLB update entry is valid or not - * - ``is_4M`` - - logic - - Indicates if the TLB entry corresponds to a 4MB page + * - ``is_page`` + - logic [PT_LEVELS-2:0][HYP_EXT:0] + - Indicates if the TLB entry corresponds to a any page at the different levels. When Hypervisor extension is used it includes information also for the G-stage. * - ``vpn`` - - logic[19:0] - - Virtual Page Number (VPN) used for updating the TLB, consisting of 20 bits + - logic[VPN_LEN-1:0] + - Virtual Page Number (VPN) used for updating the TLB * - ``asid`` - - logic[8:0] - - Address Space Identifier (ASID) used for updating the TLB, with a length of 9 bits for Sv32 MMU + - logic[HYP_EXT:0][ASID_WIDTH[0]-1:0] + - Vector 0 is the Address Space Identifier (ASID) used for updating the TLB. Vector 1 is the analogous one for virtual supervisor or hypervisor when Hypervisor extension is enabled. * - ``content`` - - riscv::pte_sv32_t - - Content of the TLB update entry, defined by the structure + - pte_cva6_t [HYP_EXT:0] + - Content of the TLB update entry (both for g and s stage when applicable), defined by the structure .. raw:: html -

Table 8: SV32 PTE Struct (riscv::pte_sv32_t)

+

Table 8: SV32 PTE Struct (riscv::pte_cva6_t)

.. list-table:: :header-rows: 1 @@ -585,8 +628,8 @@ The inputs and output signals of the TLB are shown in the following two figures. - Description * - ``ppn`` - - logic[21:0] - - 22 bit Physical Page Number (PPN) + - logic[riscv::PPNW-1:0] + - Physical Page Number (PPN) * - ``rsw`` - logic[1:0] @@ -644,7 +687,7 @@ The inputs and output signals of the TLB are shown in the following two figures. TLB Entry Fields -The number of TLB entries can be changed via a design parameter. In 32-bit configurations of CVA6 only 2 TLB entries are instantiated. Each TLB entry is made up of two fields: Tag and Content. The Tag field holds the virtual page number (VPN1, VPN0), ASID, page size (is_4M) along with a valid bit (VALID) indicating that the entry is valid. The SV32 virtual page number, which is supported by CV32A6X, is further split into two separate virtual page numbers VPN1 and VPN0. The Content field contains two physical page numbers (PPN1, PPN0) along with a number of bits which specify various attributes of the physical page. Note that the V bit in the Content field is the V bit which is present in the page table in memory. It is copied from the page table, as is, and the VALID bit in the Tag is set based on its value.The TLB entry fields are shown in **Figure 2**. +The number of TLB entries can be changed via a design parameter. Each TLB entry is made up of two fields: Tag and Content. The Tag field holds the virtual page number, ASID and page size along with a valid bit (VALID) indicating that the entry is valid. The virtual page number, is further split into several separate virtual page numbers according to the number of PT_LEVELS used in each configuration. The Content field contains the physical page numbers along with a number of bits which specify various attributes of the physical page. Note that the V bit in the Content field is the V bit which is present in the page table in memory. It is copied from the page table, as is, and the VALID bit in the Tag is set based on its value.The TLB entry fields are shown in **Figure 2**. .. figure:: ../images/cva6_tlb_entry.png :name: **Figure 5:** Fields in CVA6 TLB entry @@ -672,8 +715,10 @@ This function takes in the virtual address and certain other fields, examines th * **Validity Check:** For a TLB hit, the associated TLB entry must be valid . * **ASID and Global Flag Check:** The TLB entry's ASID must match the given ASID (ASID associated with the Virtual address). If the TLB entry’s Global bit (G) bit is set then this check is not done. This ensures that the translation is either specific to the provided ASID or it is globally applicable. -* **Level 1 VPN match:** SV32 implements a two-level page table. As such the virtual address is broken up into three parts which are the virtual page number 1, virtual page number 0 and displacement. So the condition that is checked next is that the virtual page number 1 of the virtual address matches the virtual page number 1(VPN1) of the TLB entry. -* **Level 0 VPN match or 4-Mega Page:** The last condition to be checked, for a TLB hit, is that the virtual page number 0 of the virtual address matches the virtual page number 0 of the TLB entry (VPN0). This match is ignored if the is_4M bit in the Tag is set which implies a super 4M page. +* **Level VPN match:** CVA6 implements a multi-level page table. As such the virtual address is broken up into multiple parts which are the virtual page number used in the different levels. So the condition that is checked next is that the virtual page number of the virtual address matches the virtual page number of the TLB entry at each level. +* **Page match:** Without Hypervisor extension, there is a match at a certain level X if the is_page component of the tag is set to 1 at level PT_LEVELS-X. At level 0 page_match is always set to 1. For the Hypervisor extension ... **(MORE COMPLEX, THINK HOW TO EXPLAIN THIS)** + **Level match** The last condition to be checked at each page level, for a TLB hit, is that there is a vpn match for the current level and the higher ones, together with a page match at the current one. E.g. If PT_LEVELS=2, a match at level 2 will occur if there is a VPN match at level 2 and a page match at level 2. For level 1, there will be a match if there is a VPN match at levels 2 and 1, together with a page match at level 1. + All the conditions listed above are checked against every TLB entry. If there is a TLB hit then the corresponding bit in the hit array is set. **Figure 3** Illustrates the TLB hit/miss process listed above. @@ -835,7 +880,7 @@ Shared Translation Lookaside Buffer The CVA6 shared TLB is structured as a 2-way associative cache, where the virtual address requiring translation is compared with the set indicated by the virtual page number. The shared TLB is looked up in case of an Instruction TLB (ITLB) or data TLB (DTLB) miss, signaled by these TLBs. If the entry is found in the shared TLB set, the respective TLB, whose translation is being requested, is updated. If the entry is not found in the shared TLB, then the processor has to perform a page table walk. Once the processor obtains a PPN corresponding to the VPN, the shared TLB is updated with this information. If the physical page is not found in the page table, it results in a page fault, which is handled by the operating system. The operating system will then place the corresponding physical page in memory. -The inputs and output signals of the shared TLB are shown in the following two figures. +The input and output signals of the shared TLB are shown in the following two figures. .. figure:: ../images/shared_tlb_in_out.png :name: **Figure 14:** Inputs and outputs of CVA6 shared TLB @@ -877,26 +922,27 @@ The inputs and output signals of the shared TLB are shown in the following two f * - ``flush_i`` - in - Controller - - logic + - logic [HYP_EXT*2:0] - TLB flush request - * - ``enable_translation_i`` + * - ``enable_translation_i`` - in - - CSR Regfile - - logic - - CSRs indicate to enable Sv32 + - CSR RegFile + - logic [HYP_EXT*2:0] + - Bit 0 indicates address translation request for instruction. In Hypervisor mode, bit 1 enables virtual memory translation for instrucionts, and bit 2 indicates the virtualization mode state * - ``en_ld_st_translation_i`` - in - - CSR Regfile + - CSR RegFile - logic - - Enable virtual memory translation for load/stores + - Bit 0 indicates address translation request for load or store. In Hypervisor mode, bit 1 enables virtual memory translation for load or store, and bit 2 indicates the virtualization mode at which load and stores should happen + * - ``asid_i`` - in - CSR Regfile - - logic - - ASID for the lookup + - logic [HYP_EXT*2:0][ASID_WIDTH[0]-1:0] + - Vector 0 is the ASID for the lookup. Vectors 1 and 2 are the analogous one for virtual supervisor and hypervisor when Hypervisor extension is enabled. * - ``itlb_access_i`` - in @@ -913,7 +959,7 @@ The inputs and output signals of the shared TLB are shown in the following two f * - ``itlb_vaddr_i`` - in - Cache Subsystem - - logic[31:0] + - logic [riscv::VLEN-1:0] - Virtual address lookup in ITLB * - ``dtlb_access_i`` @@ -931,19 +977,19 @@ The inputs and output signals of the shared TLB are shown in the following two f * - ``dtlb_vaddr_i`` - in - Load/Store Unit - - logic[31:0] + - logic [riscv::VLEN-1:0] - Virtual address lookup in DTLB * - ``itlb_update_o`` - out - ITLB - - tlb_update_sv32_t + - tlb_update_cva6_t - Tag and content to update ITLB * - ``dtlb_update_o`` - out - DTLB - - tlb_update_sv32_t + - tlb_update_cva6_t - Tag and content to update DTLB * - ``itlb_miss_o`` @@ -973,7 +1019,7 @@ The inputs and output signals of the shared TLB are shown in the following two f * - ``shared_tlb_vadd_o`` - out - PTW - - logic[31:0] + - logic [riscv::VLEN-1:0] - Virtual address lookup in shared TLB * - ``itlb_req_o`` @@ -985,7 +1031,7 @@ The inputs and output signals of the shared TLB are shown in the following two f * - ``shared_tlb_update_i`` - in - PTW - - tlb_update_sv32_t + - tlb_update_cva6_t - Updated tag and content of shared TLB .. raw:: html @@ -1003,17 +1049,13 @@ The inputs and output signals of the shared TLB are shown in the following two f - Type - Description - * - ``is_4M`` - - logic - - Indicates if the shared TLB entry corresponds to a 4MB page. - - * - ``vpn1`` - - logic[9:0] - - Virtual Page Number (VPN) represents the index of PTE in the page table level 1. + * - ``is_page`` + - logic [PT_LEVELS-2:0][HYP_EXT:0] + - Indicates if the shared TLB entry corresponds to a any page. When Hypervisor extenxion is used it includes information for G-stage too. - * - ``vpn0`` - - logic[9:0] - - Virtual Page Number (VPN) represents the index of PTE in the page table level 0. + * - ``vpn`` + - logic[PT_LEVELS+HYP_EXT-1:0][(VPN_LEN/PT_LEVELS)-1:0] + - Virtual Page Number (VPN) represents the index of PTE in each page table level. * - ``asid`` - logic @@ -1023,7 +1065,7 @@ The inputs and output signals of the shared TLB are shown in the following two f Shared TLB Entry Structure -Shared TLB is 2-way associative, with a depth of 64. A single entry in the set contains the valid bit, tag and the content. The Tag segment stores details such as the virtual page number (VPN1, VPN0), ASID, and page size (is_4M). The Content field contains two physical page numbers (PPN1, PPN0) along with a number of bits which specify various attributes of the physical page. +Shared TLB is 2-way associative, with a depth of 64. A single entry in the set contains the valid bit, tag and the content. The Tag segment stores details such as the virtual page number, ASID, and page size. The Content field contains the physical page numbers along with a number of bits which specify various attributes of the physical page. .. figure:: ../images/shared_tlb.png :name: **Figure 15:** CVA6 Shared TLB Structure @@ -1144,7 +1186,7 @@ If all ways are valid, a random replacement policy is employed for the replaceme Page Table Walker ----------------- -The "CVA6 Page Table Walker (PTW) for MMU Sv32" is a hardware module developed for the CV32A6 processor architecture, designed to facilitate the translation of virtual addresses into physical addresses, a crucial task in memory access management. +The "CVA6 Page Table Walker (PTW)" is a hardware module designed to facilitate the translation of virtual addresses into physical addresses, a crucial task in memory access management. .. figure:: ../images/ptw_in_out.png :name: **Figure 19:** Input and Outputs of Page Table Walker @@ -1164,7 +1206,7 @@ The PTW module operates through various states, each with its specific function, Key Features and Capabilities -Key features of this PTW module include support for two levels of page tables (LVL1 and LVL2) in the Sv32 standard, accommodating instruction and data page table walks. It rigorously validates and verifies page table entries (PTEs) to ensure translation accuracy and adherence to access permissions. This module seamlessly integrates with the CV32A6 processor's memory management unit (MMU), which governs memory access control. It also takes into account global mapping, access flags, and privilege levels during the translation process, ensuring that memory access adheres to the processor's security and privilege settings. +Key features of this PTW module include support for multiple levels of page tables (PT_LEVELS), accommodating instruction and data page table walks. It rigorously validates and verifies page table entries (PTEs) to ensure translation accuracy and adherence to access permissions. This module seamlessly integrates with the CVA6 processor's memory management unit (MMU), which governs memory access control. It also takes into account global mapping, access flags, and privilege levels during the translation process, ensuring that memory access adheres to the processor's security and privilege settings. .. raw:: html @@ -1222,7 +1264,7 @@ In addition to its translation capabilities, the PTW module is equipped to detec * - ``ptw_error_o`` - out - MMU - - logic + - logic [HYP_EXT*2:0] - Output signal indicating that an error occurred during PTW operation * - ``ptw_access_exception_o`` @@ -1231,7 +1273,25 @@ In addition to its translation capabilities, the PTW module is equipped to detec - logic - Output signal indicating that a PMP (Physical Memory Protection) access exception occurred during PTW operation. - * - ``lsu_is_store_i`` + * - ``enable_translation_i`` + - in + - CSR RegFile + - logic [HYP_EXT*2:0] + - Bit 0 indicates address translation request for instruction. In Hypervisor mode, bit 1 enables virtual memory translation for instrucionts, and bit 2 indicates the virtualization mode state + + * - ``en_ld_st_translation_i`` + - in + - CSR RegFile + - logic + - Bit 0 indicates address translation request for load or store. In Hypervisor mode, bit 1 enables virtual memory translation for load or store, and bit 2 indicates the virtualization mode at which load and stores should happen + + * - ``hlvx_inst_i`` + - in + - Store / Load Unit + - logic [HYP_EXT:0] + - Indicates that Instruction is a hypervisor load store with execute permissions + + * - ``lsu_is_store_i`` - in - Store Unit - logic @@ -1252,7 +1312,7 @@ In addition to its translation capabilities, the PTW module is equipped to detec * - ``shared_tlb_update_o`` - out - Shared TLB - - tlb_update_sv32_t + - tlb_update_cva6_t - Updated tag and content of shared TLB * - ``update_vaddr_o`` @@ -1263,9 +1323,9 @@ In addition to its translation capabilities, the PTW module is equipped to detec * - ``asid_i`` - in - - CSR RegFile - - logic[ASID_WIDTH-1:0] - - ASID for the lookup + - CSR Regfile + - logic [HYP_EXT*2:0][ASID_WIDTH[0]-1:0] + - Vector 0 is the ASID for the lookup. Vectors 1 and 2 are the analogous one for virtual supervisor and hypervisor when Hypervisor extension is enabled. * - ``shared_tlb_access_i`` - in @@ -1294,14 +1354,14 @@ In addition to its translation capabilities, the PTW module is equipped to detec * - ``satp_ppn_i`` - in - CSR RegFile - - logic[riscv::PPNW-1:0] - - PPN of top level page table from SATP register + - logic [HYP_EXT*2:0][riscv::PPNW-1:0] + - PPN of top level page table from SATP register (bit 0), VSATP register (bit 1 when Hypervisor Extension is enabled) and HGATP (bit 2 when Hypervisor Extension is enabled). * - ``mxr_i`` - in - CSR RegFile - - logic - - Make Executable Readable bit in xSTATUS CSR register + - logic [HYP_EXT:0] + - Bit 0 is the Make Executable Readable bit in xSTATUS CSR register. Bit 1 is the analogous one for virtual supervisor when Hypervisor extension is enabled. * - ``shared_tlb_miss_o`` - out @@ -1324,8 +1384,8 @@ In addition to its translation capabilities, the PTW module is equipped to detec * - ``bad_paddr_o`` - out - MMU - - logic[riscv::PLEN-1:0] - - Bad Physical Address in case of access exception + - logic[HYP_EXT:0][riscv::PLEN-1:0] + - Bad Physical Address in case of access exception. Same at G stage when Hypervisor is enabled. .. raw:: html @@ -1423,10 +1483,10 @@ In addition to its translation capabilities, the PTW module is equipped to detec Page Table Walker is implemented as a finite state machine. It listens to shared TLB for incoming translation requests. If there is a shared TLB miss, it saves the virtual address and starts the page table walk. Page table walker transition between 7 states in CVA6. -* **IDLE:** The initial state where the PTW is awaiting a trigger, often a Shared TLB miss, to initiate a memory access request. +* **IDLE:** The initial state where the PTW is awaiting a trigger, often a Shared TLB miss, to initiate a memory access request. In the case of the Hypervisor extension, the stage to which the translation belongs is determined by the enable_translation_i and en_ld_st_translation_i signals. There are 3 possible stages: G_INTERMED_STAGE, G_FINAL_STAGE and S_STAGE. When Hypervisor is not enabled PTW is always in S_STAGE. * **WAIT_GRANT:** Request memory access and wait for data grant -* **PTE_LOOKUP:** Once granted access, the PTW examines the valid Page Table Entry (PTE), checking attributes to determine the appropriate course of action. -* **PROPOGATE_ERROR:** If the PTE is invalid, this state handles the propagation of an error, often leading to a page-fault exception due to non-compliance with access conditions +* **PTE_LOOKUP:** Once granted access, the PTW examines the valid Page Table Entry (PTE), checking attributes to determine the appropriate course of action. Depending on the STAGE determined in the previous state, pptr and other atributes are updated accordingly. +* **PROPOGATE_ERROR:** If the PTE is invalid, this state handles the propagation of an error, often leading to a page-fault exception due to non-compliance with access conditions. * **PROPOGATE_ACCESS_ERROR:** Propagate access fault if access is not allowed from a PMP perspective * **WAIT_RVALID:** After processing a PTE, the PTW waits for a valid data signal, indicating that relevant data is ready for further processing. * **LATENCY:** Introduces a delay to account for synchronization or timing requirements between states. @@ -1445,24 +1505,24 @@ Page Table Walker is implemented as a finite state machine. It listens to shared In the IDLE state of the Page Table Walker (PTW) finite state machine, the system awaits a trigger to initiate the page table walk process. This trigger is often prompted by a Shared Translation Lookaside Buffer (TLB) miss, indicating that the required translation is not present in the shared TLB cache. The PTW's behavior in this state is explained as follows: -1. The top-most page table is selected for the page table walk. In the case of SV32, which implements a two-level page table, the level 1 page table is chosen. +1. The top-most page table is selected for the page table walk. In all configurations, the walk starts at level 0. 2. In the IDLE state, translations are assumed to be invalid in all addressing spaces. 3. The signal indicating the instruction page table walk is set to 0. 4. A conditional check is performed: if there is a shared TLB access request and the entry is not found in the shared TLB (indicating a shared TLB miss), the following steps are executed: - a. The address of the desired Page Table Entry within the level 1 page table is calculated by multiplying the Physical Page Number (PPN) of the level 1 page table from the SATP register by the page size (4kB). This result is then added to the product of the Virtual Page Number (VPN1), and the size of a page table entry(4 bytes). + a. The address of the desired Page Table Entry within the level 0 page table is calculated by multiplying the Physical Page Number (PPN) of the level 0 page table from the SATP register by the page size. This result is then added to the product of the Virtual Page Number, and the size of a page table entry. Depending on the translation indicated by enable_translation_i and en_ld_st_translation_i at the different levels [HYP_EXT * 2:0] the corresponding register (satp_ppn_i[HYP_EXT * 2:0] and bits of the VPN are used. .. figure:: ../images/ptw_idle.png - :name: **Figure 21:** Address of Desired PTE at Level 1 + :name: **Figure 21:** Address of Desired PTE at Level 0 :align: center :width: 68% :alt: ptw_idle - **Figure 21:** Address of Desired PTE at Level 1 + **Figure 21:** Address of Desired PTE at Level 0 .. _example: - b. The signal indicating whether it's an instruction page table walk is updated based on the ITLB miss. + b. The signal indicating whether it's an instruction page table walk is updated based on the itlb_req_i signal. c. The ASID and virtual address are saved for the page table walk. d. A shared TLB miss is indicated. @@ -1495,11 +1555,11 @@ In the **PTE_LOOKUP** state of the Page Table Walker (PTW) finite state machine, .. _example1: - b. If the PTE is valid, the state advances to the "LATENCY" state, indicating a period of processing latency. Additionally, if the "read" flag (pte.r) or the "execute" flag (pte.x) is set, the PTE is considered valid. + b. If the PTE is valid, by default, the state advances to the "LATENCY" state, indicating a period of processing latency. Additionally, if the "read" flag (pte.r) or the "execute" flag (pte.x) is set, the PTE is considered valid. -5. Within the Valid PTE scenario, the state performs further checks based on whether the translation is intended for instruction fetching or data access: +5. Within the Valid PTE scenario, the ptw_stage is checked to decide the next state. When no Hypervisor Extension is used, the stage is always S_STAGE and has no impact on the progress of the table walk. However, when the Hypervisor Extension is used, if the stage is not the G_FINAL_STAGE, it has to continue advancing the different stages before proceeding with the translation. In this case, the state machine goes back to WAIT_GRANT state. Afterwards, the state performs further checks based on whether the translation is intended for instruction fetching or data access: - a. For instruction page table walk, if the page is not executable (pte.x is not set) or not marked as accessible (pte.a is not set), the state transitions to the "PROPAGATE_ERROR" state. + a. For instruction page table walk, if the page is not executable (pte.x is not set) or not marked as accessible (pte.a is not set), the state transitions to the "PROPAGATE_ERROR" state. Otherwise, the translation is valid. In tcase that the Hypervisor Extension is enabled, a valid translation requires being in the G_FINAL_STAGE, or the G stage being disabled. .. figure:: ../images/ptw_iptw.png :name: **Figure 23:** For Instruction Page Table Walk @@ -1511,7 +1571,7 @@ In the **PTE_LOOKUP** state of the Page Table Walker (PTW) finite state machine, .. _example2: - b. For data page table walk, the state checks if the page is readable (pte.r is set) or if the page is executable only but made readable by setting the MXR bit in xSTATUS CSR register. If either condition is met, it indicates a valid translation. If not, the state transitions to the "PROPAGATE_ERROR" state. + b. For data page table walk, the state checks if the page is readable (pte.r is set) or if the page is executable only but made readable by setting the MXR bit in xSTATUS CSR register. If either condition is met, it indicates a valid translation. If not, the state transitions to the "PROPAGATE_ERROR" state. When Hypervisor Extension is enabled, a valid translation also requires that it is in the G_FINAL_STAGE or the G stage is not enabled. .. figure:: ../images/ptw_dptw.png :name: **Figure 24:** Data Access Page Table Walk @@ -1532,7 +1592,7 @@ In the **PTE_LOOKUP** state of the Page Table Walker (PTW) finite state machine, **Figure 25:** Data Access Page Table Walk, Store requested -6. The state also checks for potential misalignment issues in the translation: If the current page table level is the first level (LVL1) and if the PPN0 of in PTE is not zero, it indicates a misaligned superpage, leading to a transition to the "PROPAGATE_ERROR" state. +6. The state also checks for potential misalignment issues in the translation: If the current page table level is the first level and if the PPN of in PTE is not zero, it indicates a misaligned superpage, leading to a transition to the "PROPAGATE_ERROR" state. .. figure:: ../images/ptw_mis_sup.png :name: **Figure 26:** Misaligned Superpage Check @@ -1543,7 +1603,7 @@ In the **PTE_LOOKUP** state of the Page Table Walker (PTW) finite state machine, **Figure 26:** Misaligned Superpage Check 7. If the PTE is valid but the page is neither readable nor executable, the PTW recognizes the PTE as a pointer to the next level of the page table, indicating that additional translation information can be found in the referenced page table at a lower level. -8. If the current page table level is the first level (LVL1), the PTW proceeds to switch to the second level (LVL2) page table, updating the next level pointer and calculating the address for the next page table entry using the Physical Page Number from the PTE and the index of the level 2 page table from virtual address. +8. If the current page table level is not the last level, the PTW proceeds to switch to the next level page table, updating the next level pointer and calculating the address for the next page table entry using the Physical Page Number from the PTE and the index from virtual address. Depending on the level and ptw_stage, the pptr is updated accordingly. .. figure:: ../images/ptw_nlvl.png :name: **Figure 27:** Address of desired PTE at next level of Page Table @@ -1553,8 +1613,8 @@ In the **PTE_LOOKUP** state of the Page Table Walker (PTW) finite state machine, **Figure 27:** Address of desired PTE at next level of Page Table -9. The state then transitions to the "WAIT_GRANT" state, indicating that the PTW is awaiting the grant signal to proceed with requesting the next level page table entry. -10. If the current level is already the second level (LVL2), an error is flagged, and the state transitions to the "PROPAGATE_ERROR" state, signifying an unexpected situation where the PTW is already at the last level page table. +9. The state then transitions to the "WAIT_GRANT" state, indicating that the PTW is awaiting the grant signal to proceed with requesting the next level page table entry. If Hypervisor Extension is used and the page has already been accessed, is dirty or is accessible only in user mode, the state goes to PROPAGATE_ERROR. +10. If the current level is already the last level, an error is flagged, and the state transitions to the "PROPAGATE_ERROR" state, signifying an unexpected situation where the PTW is already at the last level page table. 11. If the translation access is found to be restricted by the Physical Memory Protection (PMP) settings (allow_access is false), the state updates the shared TLB update signal to indicate that the TLB entry should not be updated. Additionally, the saved address for the page table walk is restored to its previous value, and the state transitions to the "PROPAGATE_ACCESS_ERROR" state. 12. Lastly, if the data request for the page table entry was granted, the state indicates to the cache subsystem that the tag associated with the data is now valid. From 4303d1b852ecd9d16f1f09db8ffddf646b7f27ec Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 16 Feb 2024 17:15:25 +0100 Subject: [PATCH 087/182] diagrams for design document updated --- .../images/cva6_tlb_entry.png | Bin 150816 -> 56731 bytes docs/04_cv32a6_design/images/cva6_tlb_hit.png | Bin 253826 -> 71291 bytes docs/04_cv32a6_design/images/in_out_tlb.png | Bin 34651 -> 36536 bytes docs/04_cv32a6_design/images/mmu_in_out.png | Bin 33165 -> 69538 bytes docs/04_cv32a6_design/images/ptw_in_out.png | Bin 191811 -> 61994 bytes docs/04_cv32a6_design/images/ptw_nlvl.png | Bin 22459 -> 441274 bytes docs/04_cv32a6_design/images/ptw_pte_1.png | Bin 13635 -> 21586 bytes .../images/sfence_vaddr_asid.png | Bin 32736 -> 52082 bytes .../images/sfence_vaddr_x0.png | Bin 25982 -> 38915 bytes .../images/sfence_x0_asid.png | Bin 30241 -> 50819 bytes docs/04_cv32a6_design/images/sfence_x0_x0.png | Bin 16941 -> 26869 bytes .../images/shared_tlb_in_out.png | Bin 202926 -> 55618 bytes .../source/cv32a6_execute.rst | 4 ++-- 13 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/04_cv32a6_design/images/cva6_tlb_entry.png b/docs/04_cv32a6_design/images/cva6_tlb_entry.png index cb691d6e877012990cbe11e19db92488b5971135..3ef5140772972b5050fff1c61d57b945a536149d 100644 GIT binary patch literal 56731 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 diff --git a/docs/04_cv32a6_design/images/cva6_tlb_hit.png b/docs/04_cv32a6_design/images/cva6_tlb_hit.png index d24b61e5c0daf8b274ff954caa60a85ab2067799..5773d3cd8ef2e99a23bc7c87041ea363ad07f865 100644 GIT binary patch literal 71291 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 JS?83{1OPSIz*+zR literal 253826 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 T@I~=l!f9EU@`YO4aF{o%=@QmcQ0oOr z?+1yqvM9kkVcM~W;$R5@k2&Vqu4`HofWB1oOYH^Zbvy~+A01g7Fd&tle%raYK=2Zz zT>KFW_j}?zM6Y*a?|Vg#{4Phm40v0+x%W`4_HE+%#^%envS<6qG~e$o+zmn?;+Rc- zYRD(B#rY^nlq)o8m)#}V9K|S!7V@i#6VT^*{nzImHd<2Tcpx`*y6(^(&CjL34I7yTrvXHp`&Rx=;Tk{+SI~PhaR0lj)Q~-Z)_s!? zNsOHk3YRQ(jSnz;&7b&gUQP#ry2$S(=6`V&8Lc4m@80g-!Gxz-Ze+oxAUOUJ3L{I9 zDEI?LN-*CERKD#AjL6ieOxI1;k<4;W($kU7wH@depWm%8R~t1q%ctKHgȁ*{6w%($ahAVX0H!bgoct-FQH*pxc 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 z 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μKtgeks^Jb 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 XwFTT6bn+_Q1N^`)!Y>q_cX;$4E~OGi diff --git a/docs/04_cv32a6_design/images/in_out_tlb.png b/docs/04_cv32a6_design/images/in_out_tlb.png index 31858be9d8f0820403f51f2b565876353f28d6f8..b85d5d8f060978ff19e8fe9de906ae73140c83bf 100644 GIT binary patch delta 19313 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 delta 18570 zcmZTw1yoewww|FwkRhc+=@>v@2x+8Cy1Tnu;($4{(hVZr-KCV$(wzbl64Lz+H`crN zy~SEHv;Onv{&$}JZ5q;1=AuwOl2QJ^!hk>^6b{Ex;1|?GUG@c}Y=~+L0+AU1#31*G zhy9rVI~R{87lMIDg7-Q2f#BjmaB+Wn2M3hwoV|P*xS#n7@R{%-SioNruC9(&I#%Y2 zcAgB}GQ2$e96WqrGqa+crm_+Pw?y&g?d{=c?q|sbwu7C#t=v8A zT%7;ekDH5wk3-;}4?O)`t^V0+ncB+EBQ8Vxw^Ts$WxQLjSRV8!9*7Kc_Q#5`z_M#R-e;W*~}9 z;}WWV$`_dcaI&!Fveg2}E8>Letmy&jNabh}HzgfmL{j(KI^-Umeo4KFB+~bUG}E(VOY@ z&CI*7ciFG0rxzj@LW}~2_vb}?8`6?_^7qn`nOV{7xWMj~Tny2$BP zgOTU{QrE)|?8S7X0-n)&M>1R>}{CJl~spJb#>CNPQl#TkiN=N6~@M7Fq2RulT_Cs?Wzj8 zZx98HhV4w`iy4l07?Mo7%Z+Yzc0qXpc=GCH7I_!q^oW7bwkfs#Gi1<@ZuKK_T7)(V zi3}&xnPH<{RfHWDAblOA(!^**iC{u9T}HI4oUp@f(rq9}>YAPQ@$~9qORn+#roH~P zX|YFBGnBNe4i;{SM6z%1bU6VI(t&!qRYurh77{{*a&6A<^y>z82+SgVa@sNMBE>`8vy}8&+E9arG1K!C+z?gT8ADxvXRp zpNn6w!5@06A-KY#XjA0<>*X-F&*l>m#gaHjcQRcJeVh*?kTfz~Janu6Ft-ro`B&nb zbXM9RfD)D;gakwE7#RdA8};Mm#J3-3S2_b zP%?#LR$olRNkxH)L%omBGm70^1At*I|D^77Bw1M5@ilLx>Xn3@c4j zjNu{H1Y?WCiE^No1E1nkylc0^=;?$;x<0b&dU(m&f2o<>xF^~X=hJrM>@-9 zsYU9w@}Sv5J$G6tj@^MhA9GaV&JnVvbQ(h=H&kp$eO9s zSh@*k0=WiL5<+3fqi{>3s&FAB-ryj$OA4EcKrRuNrLakLEzqu_!w!p(0&rSYk*Oc) zIA1oj=vISZpJ%H*M2nnrk`iGVDursu$K}>&@OV7er^e>0gv|A z;I(>WHaKx&H`e+a@e?)8+jRONw~xF3!iL@W&?G2c)TSnO zh5{T3qdrZkZHp)1E}B^=hk}Vf1=Igj7h%KhIl#mgtZElqtOR5|mYZj$ZKHvyAdzFT zXlJvC86ny-64(?6zx@t!Ra6ps^A71sySUO7=M8eNU4DLx1NuE1ho~FyJx9xGl#$vs z;r>xHrTqdOlI0boA$47-%Y!N1^+9o^*N&C&$gEblVBXhPN?3^2*d$o7Wg~lx;>S+! z2&mG72+{#NP|$cL9)9!kzC#jJv@a%6q-z4|7rB4sfc`1Y%8m6tafAa+_r!oA@9xP@W zsrjkCSWO5jffA|GQi_Ek@i7GnKaDRKLu9ZIQeB^6htsRZj<24CFp3r2t2*Fb*L`|0 zl73ocLqV_~gTc|MGJz)lXy}b%fDB>Nt*)oy_cD&a&-VEZQYFAs^DsO7s4t@& zfnS_VHZ|4-&thhICCIV54Vs4Da}VAC(f6kESxBDH!oysf3DPdRxE&af2z};tvNW1a-dA#VAaZX5 zYo0r3UO~!VXiA4Niqs0Vl}w@0#pX zSMQIcx;khl5)I6xB7-hw4T~oC2I!1x+QNv36$rq2k5rMt(piTu>mn! z6f(S55P0$ta+hTsg)AKDGB_qK%A{tw$A^l1`i+1GHz2cn{UyV zGUTQm=kN!_rCRE8mT?TPZut9d@X0TuN#2GKm3ENPtzNBSw;O!}hcOs4Bk z|5M35&P~Y-{frA^v)^f?%kBPBu9;gr44_jG!aEA?&L@_Ocq(}sM{sjkw#yE;I&Xl7 zqxve&A^1Z}t7V=DD{We-X8j@N(UXs;_s>Xkgr2jvz&3mFBog>rUn+gNi*lE4rV!}O9E#+(-aSAsk^b%Q_E z_*sZG7SH0C4>%HVUY#7wR*Pw8@2=!|mb#yQ-0YS60t0b`QnWeydKF42L@%YzCJoac8kiHEN|XCma8GNEmr9dmj4y{%@` z_%r^V$d=Z!>G^SC#9(!vz2hOJvzmSBXXa^VP&qGH2ShH`Lv2qZ0pthLDAuy2I0gq$ zS4)1j&DuA_f6P~?Yo^RQk#jV}HZ7Fv+U~fB@#@+TMLC+Od|%Q9LJ_|}EwE@cxadZ{ z6RD$~T|8FAfQ^97i%mZpo^~CnxV%-xDi|txWYf0`o;ewGw7OytBM`(xPB)o%9#irO zm5av7d$^O$P)k!kk+Hs{ zfJkqJUz%`QhE0dK*?ms$9od0@#-VhYO2sAaBQn&VAvx2>=2lAvyYQfd%t#Yp1NIHi$X3*S}Uspc_I~aJnUF_Z-q!s2eqB&Qa3% zcJRE-WN}MGOqj*EPi#o@8h94CZ!&Ja=u(yX4_ddN`2N2yFOtsYv)ka>Se)i*mx!Q9k~C;I_0eYn;mT zHhITFz^+p-HkqkX_96Yqv2E3RzHy4I6&WAWfu)>Lh547aWlr=^xmu)0=>+!9+8WVq znbz>R|4dp?k?;dKD(nKj?#V)j=+dXtuN#MF%lHk!AU0CBx=OnwTZn{BhOpxn20Vop z%I8H^?-?5IsN@Wbcf!$0hog|x3L8n(=u?ThIO3nG^1Xr)wBI;pj+P^FR+F8ozPA&ils24l*uFy;>c-=lB2ngaP+<8 z*bB54kru=Y6#gV`^s%1Cg9oSNw!6Z*FciPIW}ibpZht*m#-C+UN;38|ODso|AG&yZ zUUPkoW^*HZg6e4)`$x@aj*IqI(_2J^?MAJoAtFxhZ1%#iZ1;T%<2s;-0U)apb83XP zb!#Q%KZK?rc(Y!|f9l!Y@Pb$G?(~Go=T(GZ%HK{HUZSYBfuTB!UdjAPCi`S)eKiRK*{}2_MCmjo0tmAHgPg zXBbh=R9J*i&nn|~73e7-d^CyjPTb{QoECTo*a95xq&C}KWm|Zlu@vN*Gt=c2r#tC)`_VS zy^n=58>hYLeh?xSlrIQEWFSf}AUxj1@5*Zw#HZ)azFOH-Pgv;Z-xAtR+VR!R=#rwP zOc94$#!RB^2>~L0OXjRXMyc;H5Cg-Tai~e7Q5S3)+Qm>eCg81@<46)?G({H_2?f7wsw~O^~S?N+&l9vwl=+3B`cm6_?n?U<>;$47^L$gV&uxO zHG0YYSR{$f&?B_9*47iPkVhRNsia*$2oJ~hMxhEB8U#Qio~d$g#FGt9VzA%RQ#qfU zv8$PlF8MfNf-eS!lH@qOUF~@|Z@7c%!}zGJi+cEOJx2HEZk-d{hTQkFElPJ3f8a8G z3iUu`f+q(;K6GoyqYTaiI~U8@|Csg7(9MsW;-R_os{|`tSIhI4@m{DDsNRg-z*^dI zHE_$3X`%$gdM@Q?G+ufOtu4h8{T#w)c1#iHIZ@$q(XGY68JUJxw(SJ*8BwlQk1ZB9 z?InHi5T96Tz{`DQ9~G-Pq4&z-XUU~O|7l`5G#8QdQ^Ayggh==05Pp`|sRS6#nC#E2 zMo=(|YdGn%C*6u{*^JdT*=Wy93P$vX0KVuiT5V2z_VWK4+E?-R3F;%pdeWw*6j2S+ z`|Jre6B_y<135fOTY?7VjdR*xkyk6Tx1eH}zgY4pl`nD8(< zI$W_@K*lrHUJ(%Oyc)RPxCw+r zqXUJA&b+Xyo%8qDh#dj8IZGwbr&ab`3Xz$?ls@Sq`!nD=w}ux3I@}9$Kfs zVqIK-n{zkRR$K!c*bWX)q_o$$+Wpz@&)^o6O|%PohceztB{W8w6x76 zkisW4I1^!0l+(P}3TP6~_viSxct-;EJTk+}ftww-9fKu70AK9-pkaQUgG}u1VtDO= zjew-^&6Y}Z>eBUw+hlbg#3;Sxq|_x+R%Bc4FqgB?b_w2owXnb1ymaGixUDWnP1knw z$E8kqeu%(=!lEb*2`aRDb{BW)K&TUeIv>sFuCZ{S@kNfrm;_G2Fc;J zM!6(V=EaUy#eyd;ncR@$cD!OlVnH|S^%F*b z6NQ`m;nZ1oSZH7GkM^yDr+kA)BxKX{k6i;RUtXVVdl)KlFa7rKTVXsdOFF~SA&=F6 zEa)?T*5AA91E(z$8dfGsw&u6{y79a;oullG!nTLxbQx33_t)#o>0Ulj=;Y9#vv+kv zugD%(mAmovDp_y7=t-f#(!~(j2P$_tpk-UDJWL~PmG}^3ZC(!V%J6QFjH9%MPU<}Z zn)41!nXHqfUSVrAZ5QBbPBvkbN%Sz(ca=OZ6Pi8%vSaM$*H+WqHG>*^x`k<)35 z<6S3bkJM?0^48U-55KOHR4xTwMed1!d`Q!NceB41jEes##CEKq>D)G90u-oc57`eO z=z&L{*Y{1PNH6DzqdRLpPv?Z499R#mm72B|2=4FC&4vV5q7YlC)GBTIT(X^-`nP2F}{IP zR3`JJZz&zdBwkNdvKsQxxHgEG(%&YYCEl6dqZ<=Y6#vX4RnH| zd6zIcaMxn(J%5YY^)Du|&|>40BDyC+o6m=zqmY%=g0n2pEzbp>Ev!8R&dZs!1LOlX zUrjYH{@%P=x&v;1w)P4w9?DG4xJCQV`)+Gw&I**Z1-h=Sl|m^(%EZW~$h{SC1>Jlw7%ej-PZ+GYTxC^ZpVUY45+RYz1d|E1s08|KUC2@+VfqqkE_3q2 zi4=8yLs`0pZ9StdH|(_&$KuY!qX5)EjFg8F$wj0Gen3N;9sph>S(A?NLC76md?f}wmL@io5Mv1B=!g=SHhSf>*gA_!=nmND@Vl%i zciT0v&Kcbi0^w}8A+cNQViI+0StTh)YNYp35Fdg{(&1Ornjos2Ps6*a{^V~|m@*~a&?1Qa0t1!L` ztVLF2h5KusG(#|SCTgn9$(SpR{^;&HQ2;iF+7;ZY0|uNW{j#JgmO=WCgFpM-sQ7FyenL7wyyweZ4A>@0RcjCp4jZ(XVh816s8Genz8 zYW60ClefLhv!#A0K(pyV_oCgad064eN0n09TCCB`}JI^m{_ivi3T84 z)vyOB6f=FJ8$L?LP!?;nTM~#LO|{>8#o3)k`LtMAx_!L2b#74}X>_;8`C^fz{(s+b z20Z2PpXrYZVv^k52kx$ZKi}gZ{jTin9&3uORR&+S`r6*7RmNSVjrPZ?+O64WjbiE% z1FhjIVd5jJv`SG3fzDif8NDI-7a%hODqiy0F#>59Z`+3o>qS6wXD2d5yB=+|-G{!dvT++wt}qQtN$!UOK_n463)gt6{* z?z`gLKT8(F4(m>a`x-5zrlk)|UE`rxYwJ(lqTLk3R{dc9D=%V+Zs4o`#uAV&(9b)> zV5TjsEfT#+*^t-sbq4eG@RA(IEHzYb_YT8(`;=n}TcFEO_fdyb+S}SE)ru{h<^ZSw zUl9Z3WXtZ$A(&Ap2l3rsxhsD^Ki3N(IUxL)_~c+L^A=%zv#k@o`V3BwjjGc;J0YXz zRMi%yF4Dbc?Zz4Vx92xS&%VDXq9jW+&SX#=84&S@Y_)IfQw@Tt-_JDxzaCMz>U#GQ z!q@FxPqSXW!b3dYznKJsvLn+A3SJ6<23>4roLN?v?(DVsYkb1QMI-!ze%I8E7ik4= z&{Bf#{*-k7zWBnx{r!-vz%mRqO+IZEblRT*7IU5ta8U;NCT4%v=IWvUMuH@*sK=`x3`9zIJ2ZAPsBTF|H?x7L4fL8>p4OTrG9EYGw?&Dgtiu zg6gi0Vvpx@h-@%-y?ff3E-twZoa4oL{tQ1WcX{{!!!Hgm{RI6JerL(8M<3`zw+GZ* z;3=i&i&fF<_8k(O)-^4Fx>{?WqncwJ=`-pqCiF}guuY2F;H*veSWemGP4x;~9oyX~joYd~; zXd-(?RsSsV0iWQ<$A47w;DBJe$D$11EcQ#&H?#23oCT)!*{>RRK^5Mf_j8xQET{f+<9WUn=0 zM^ab|Mb3?2R_IdQYFaIS5W8OaonNPIRNM6DigzIqT#hup7?uxZUFC*YkIGcly=g;< zBx)FP7HFwa`)#+=^^}9*iKg8G9N%tbQd{i1-2$VXz0J;|^8Z@NJm4X^Q;J_>gwnu=eQ$`G|lY@I%p@15 z&w)A%sk)DH%Qu#Q5B$^tnr_L8b}b_tzMNj3&{$^ZOTRviC{}F9YoAud%7sVim%wjH z8o?r&tk=Pv$wd#D$G7f6u=pQuqbQ}UGNnozO2>o#@iO`s#h|d#fYa(sYAv>9?c+tr zTjss3k^CY6eHK%77UG`4E6U)aYs`$p7rlZ)*3f~4MS{9srt#(vEZVp0_d=b%eXvZW zAUd1cmm!1q?-WD7#T6xc7I`HT4;z%$O15TlLoqdqL`m_6IV)r2AQ& z&moH^%WZU#<@WGJ%*68Ufs0qML8L;bzI6G>XGq+(Ee^C$2}me!Zr)WKd)C$nFYpQRT;`hFC|^Sv}vjl>p<;^vu0+Lzmz z=uuGdcOCifHEtGAK%p(3Oxj)BpQGzrtSnr7Zgxxn&;39!@Px@tL`uY??6;pS`bQt< z#&`l+ysy~qq`=dWlXZKljPYsJTMvw0T=1h0^7mVZ*=WBe_4#G%^``s9fwg;eDll)8}4sMiLj$91eYxe$Mrjc-3Ib1y%$~@C*=0d#E^oAwUQJpLa zyX|s{?*!{LRx%KpcwOiN0(%_Y%CLrZw2It65YBl-2PR%HiZ{s))q&f?44how+)2|J zO)D*!(gwFqa#(rrd&P2jzWQo*QGjog4Bljfd=!@6VtU@7>)4oDVWr*dXqwX^mR0#o zr77=MJGu@=Vz&-o>XYYCB^*b>se?hpPjsbhw1SMlqtqo~_aAOLZZ}${dBKdTW=;(~ z>3JyC+3EGo3x8y4MpNH~55FGf^{uNeyn7`32D`N&cO$U&E6MLxJ@)q7S_R;k%e@<_ zwI|`sz4z>7Y$#xZhd)XJkJr>Xz(@gaWWIFdwZMfaXJqx1I_`k=lpoO!RPdoDZ7NH~ zNv}&G7Sp9~Wi%rUb)mFGNDHRypi-VwE>4%5aI(_ribSHTcS{GOuvvZae$_hPHd5pK z(a%x$=~bM#=q8__4vliR>n8Bnw8y>(pChoGO1r2iGuvHD%gds>F!9j))yY@5!1PdX z&o?$j#Rs1WH^p+8bSA<`D(O{TjmFAMMkgsO@V2n8lCO$XO*dqxvBTgx#6)s1VFc;9 zU=xHZN_gg(8wqKr7tK)Hlt5)a?gln0bJ&7|<5_8^3D*<@qfd0sxEITq` zyW~6LN>!}j&C0_sHZujJc;XD=NW4n4V*i~URR%L;q+PpWA(X=ONk?Eix@{WU*&H-C zhWpgeL8G=VLPMDCCc36l`EkQrI%}546j~yVVUb>M!kcC9xy6@}9FjF1`h8hFB~_DN z1}icJ&Fzu%dsc@*6<|&nA|=I^``2vxNQe|SOvjlsFgs8?0oqZSM%5?&6E6COqE^0V zZtLe})q$!Il!Ak<*~P{PKJrVa{c@t9t1aufVu*wCZs*J+OUqT#28)z3>=c6ty=NuR ztS>l!t?jaL3%;P4DnA+?2HMm*KT2cNEFQe)kN7?Wd0)a=k_M=kKojnmKF|c6+s>yB zce}<`HbvwU+L$)<1%FN&H}?O-Tcu7fE{BfESXSMnD?w73+IBkU(hiG+7E-?P_L+04 zSEDL+012aw?jztc*@76d&9v$WYZkD{uE-tG*a`+&1xmnwUCF~6Rrus_vI%ik;r#J$ zoN>~w?{jQh0NaRZI~wyeTcy^En=s<<`_~N0-br^puLO2rRDVo@x_KERhb3~to~qn% zn0{;AmDCiDLB@p*`raO|`vmtM1>5N3Ec*(zUYe3p?IxfO-iPNuU;B1IF(a^gUV5aA zp)H#Q>dNV1`|6_2{Apk>7%D}YeQa_d@qK<^3$5D$FwIfcoA=wUD#tZt$%5&uK@IaL ze7UW%|B?{+RLfBMNML@IwEodgl<$w$UaSofezZ~3J8o?0L3j1#Bvt1X4TV8;#o(ga z#B8DOx<^rI=N(riAd=kD(z_T*Az5xQOvbhb`e={q{%8b(bC?i-j#}6G@toohaE+156P6$n1M1gm(b zMAY;AS$t}=(VVFTWr#i_mM%W07Zi6XnB`H*@`-4A2AR0v_nW##B|G89hskkHx zgbjl^iNM-A_PatX!#4)TGL)o-KB~h6JlZ{t32DNPD_OH#p`7_QKIqHOqJOgXDXl54 z1NxnJ3-r@}Kn|aft?n=-vL#hDPK;jsX+o^ij}tAD zmBAcn^_GLv9%(sYJB+euG)GOsV*)slW`slz7WsxHg=$oS7lVpY#*^>>_gC*b^Ts}o zV&;s-9Vcr)RmUhMEi!VQe6-wFoa86P{knc`ub3n;QcBHhQW0AvMQ*V^*N@BE;ypae zw-S@YtMiDeso%#lfN-Yur>M$4`U}P)9(a&RnmXS=dRFUN=hq$b$;R3Um|M{zcusM8y9_6eaj(ZFPQ!)3rt0?j787u|Sbwv?I-53rmVGnu_Zoi2AvoF87PsKGffOJtZ^)v31F>04 zx_A#pK{xdo4>^iJOh_Z|Z>$een6)&;?fW*Q9d_~1dS`J-A}xhO`o+t-c8Asa@>!Q{ zn`SXF`C(;2x3C#!n`{3Uxyy#HUw;9&bQih#rhil%N|DMi3HU4w=Q&6Qc+U!HO{Mdf zD7a`*D%en@i>38lfFRTAl6}^>845h+*3O8Y9}=uZIyUIz*0ao)@uY*312U)T#UUt& z35>DX&81i{K~(H-9q&}y={rIr;POkPxZOi6{m-Yziy)cC5OGuacEQEfMVf`9C_d!D z$(qlFjBLh$n&c_)rGsbsV5z=zaIw382`gnznyTDwd&0Y&$Z>7;;?U; z%scZSX`@OL@W>^u+p@$yTkaPpoRYbRuLA$VuWqOpduiJ}Zg~!|*8jR3ntlwkZ9%^F z3V0W0<5_Phj$qLv0g4J)&!P`*QqI$8QKTW}^u&;&3RdjBI5>sP)Ck%WV~3_5 zy!U|`L(eHtmbnHgIw@GsH*Vh3Vk0Dze>`rJy@!QO%4U7-j1U zs}}QxHsI_%g1mk`apVK{Tj>i>oMRvjU{YhLp{5cQ?+YgKLYjbq6;%9;r|Ok z1QjSySW{K;Sap1Z3Ui8tPblF{im*bqQvbNRXoyfa$y!(O^+9ZW!@xTni+m2qL)Fe@ z7VGK@ig2ug2_v4zq@+E<^fFFw4MzOkh_-?MH#rxfTT-G7S_Opp_e<(0=3cIaMp?NE zT#NE(4Fiv!=}k8d(gWzOiO-P*4@6a~&YOE)7nY@G2P=)$)|0Tweg47cexc5!#5ea5 zq=#JfOtp_ZNRMwIJ#00s!l015gpGiis%L-@?PGP6V;Yyw+R%%7*{-h^XH|k}u6O2A zRJyn94!hLdmrzm@ie~Tr7yW(QMa|ZRRf7kDH86{5ep)Kvz0`#u@jdcN>GWUlPf_qr zXZte9Ccq46HpRf^<~_W3zR2MG>r>Q0!Lb~L=rZmpK9%f}Is;DOYJ;iZBk|FW|H_*x zjL#PR1l2AZ`Y&Z%yN<8g#N{7kx-95k==;XfkHB;G0(@a0LKmLh+6`nVXXz;c7soOW zr8CyGZTV@SVR>a=>8tPDOf?)-cDh^c0)SFf3C#$Q~6o zKBcs9V}vz{ArFo-E0`g}`khlpvZU#Cxd(IWBTZ*3egoi3JWuDK56QzVwaKIdFU9lh zDV)-F$Y3-!ej<}z42dQD4F8-r6rET$qGm?=mjSHON2u#eH_M40g>A-y{YJKEpA{4u_n8M#o&0wUV?2G|kt)n3&!bJxq;>*| z{T_1+BSR|+4vtHjL5%m+13ce=ds^=yvOd+l`bjw7YU&oKWkaV*E%fn2<~q(h#}d{h z&K=~gA*scgUq=6yr-m_^2>01+G3{!_3wRz7cIcd|&qMY}n#7j(N3Mb&@tJ7b2-MZb zjRrP}yoW*%5k_3=}srLj(fZ3Y31GYo4_Vh{1ReLYYsNXB>L4$v&g zY$gq&EEB02gEvqndaFqiwXbPk`Ts269%fZf5q!ah_F2~A_Cb?s$-a?y&cP&JVH z6vrDn9kyAc(j_kZWgyg%QOFk)|0fg?T6IHeDpq^8Q6=XQ2&FoNE!S#Dxo$lbOVuvk zdDzV>huJkO8?Syly9Tt4ib9*jyB&s_=3i>sJO%ZvD!WPW-;o1Y#}95ScgD5qX_1S@^a?TBMTUG7 z+RBggKD#5kn(qx}A5#{}g?)xm1m%Xhn2$rkF|x9Q+AibLZ}s{-AR+N;t(;TjZRD1= zzZ22tu%DTIYT=??G#H9Q9%@D;_b@U{`|9fT91I}^B&8~RyEKB!w!iOlk|w&LGFd=1 zCtA|-K34G-k^)bv>Voxkz+#^Gjp`+|6#VuO!K_LFT+My8o{fA+yD#Pn>(L$l@0qV` zM9~wYv{+QZ!}}mM+(PJ2G%c+M;^qgzfe%Klu?kuxobs)^x^-&pdpDzGFU~;aoSGg^x8GM$l_(ym>%H4pCqehk3zR93Mskw%+t0QZH)V)d zvkqD}Yw&(7t1e{KpTYAI?-c1iVQ3R?7;fk&Gb%w7&(`PkRwV8%AqehNrq83pceM;% z3Mj2H1DJ+y*5{>TsWdyb^k8n0RpJH(Y?bHkY+J?h4VPV<^NL;kr_o50#CMcQgR~t} zEWTwg{)#c|ecOIgkOqZRtY`eqAo;DWP2)ooK6K@}B#%L$NQ*BNCRS-FjB6O-W5_s& zOC6f1Or%Ye{?p~(bgr=#yv9g@Q{ZL;5zNkrW>34yhaBN_i3F9Zvnw7p*xrgqUI}^n zzBGDp9BnMc7xU;4UTiiAs@L(*2MsGj8iY_?l2wu(E3YAXiddl~C{u@Le6jKpuiV9e8b zUqj1bj!*)W^NA2u&56uNtUa+{emmooo-%e)((r9c>L3?v1T`zr?2`@_g_(#`k9_wf zd|T%#TCPhw@!ClT9?W6g8PCYfp2VRTK|;?w#6XY5`I6Q&$P{~35w*u;lwO5J`^^2Fpk?KI1ee4seVDEbX6fYVeH*4EkA;AYwJR`52`&KSeK%I8wgI*4|JY5RkCN5v8hJA zX+mvfee{S;jq9}>m(Dg*h;?9lrxQ;V=rH67{=4#qFmB+}Crr~cJ#`j+faFcP)2kmf zw(q%jL;~(BbQsw~tlbva#=e}+aSD~u)t#pfOud%OUqy_zbz4D{7D7hQQZ78xRM_te zgcMa*)0E-3wnHXE?MNZb+K!^9yhf=W(;gyuk8(AxiWm&25}zvc37CBOp5KRxHK6%G z)!9bd47ObJTqFGTXD=sUINQO>RrC?8IiYB*I%?{sr-UPQe4o#A1{_JGiapqaSS$JZ5Xx)hz~jpFSIfd!DsE6hIIm8RL{c|LWH?NMS-Vh_TupV% zk1&`ont6r}85Q)(*sj>Y6Z2B7MKd|0^dsm?jkuq3-B;->0D+JC(I{BT!pz#tzU?54 zQcq8}J!iNS1C6CLpKJG++|*fxURpL-5EN@&a~J8bO+Jq

RfowBjj;XYcr3IbMOV zd;{h6Kt-?`ZE#d;wn;+V!cD`lYBdcon>i__ay%1RIe!@t#(;o@CogZE{=iwLO`)~S6?bK?-vmc3M=!EQ7kQXjy<{8*k#Ff+2;)0 zylL=h(Mr8nWZf^eLUt54;(XuOPxRPI@*5xmN7R5Zfgh;)W-f;ag zdZ!-5uVL1Vsh0LF(Gy$ujQztCBOZ?ZVG51bnQ5)JfOE=+ro-yJ;AA-0_(KD*jKCmw zYj;x@4B@u+zEpJRkLToTN5qq=kc2?-Y1)>{N@#C^O1$tA^zAR*uU8n|mC-H2O9Ye` zc**56;o%HqshjZeAQPq{(&|SIzDQ~6dLU-m({eN{WNjPqed0{(aMn$xU2Yr9ZaaabUv!Y5cu zc)j97OVI(a6MQmB>@z%|* z)x)mwLb6O;*CJaSUG0KTT&^hcPJ*@t6->sD5EIHMh>vkbmGhP>HiueN+)IinXm8Z|g=N zL!2kP?(br*{S)?-+#-*>9!TWfZjP0OUPD87a3p%8ej2a+ryhQb-r;X<%FE47J^B^r zd`4?2-^xe%o|LDB*67`!5XGZ5%=RpZ6lCb}Do3QQGL;}Wm>}Ztn#G^I!3SM0-sj2D z}>U4Lp7?8?6!Iw?1_{WE3deD&24S%% zHhg8m0{;S8QF?O?fuK|0|6>B_W}3&8{AY1VIPX{be4Di(FxI+J2U>(hL$2-lQ_(g1FFk+8$osM&_&L0oq@wbQmIsdk>;Aa~KTIDeMgS(NTe!``1w%!Kwk0#u?C2)tGCu9B5ojv73qT5a}HC(mU3ec={P*Vs<;nX^K z{%-5BC?RIKUt- zZ_n{wtp^UT-cR3s{INf9XyenRMc}<*HycAEt0pPU$)0qUy`awYA*(P_^_Y9N;n>T% zr7<$AVy0Z*$9w(nu5ZStcb^9?g|}My+WTX`)6$NRV$H`I{AfGvp&RWVIxPBs%<^UW z-&ni-GybX7zuf1!yldsECAR-;E&sd<$Oz3;N;bLscW(N5yK|3w?9P|Qd}gz^U-{*r z|Ho7D7aoS@8Rg7&*(oz^GV?aRkA3pXe7n=0UY;m>1=P3Qoe$iZ2Hst}?%b-PZIfK@ zq%s`aZw@>$ARujT`}>K&Hq{c~rqSiU6~CP63JmJF`Y5Mf5WHAj@k|wT*aEVx?PcU% zla*(4f&KE{X`kjz1??kN&C0!+!Q`uL`qbAOxE=HCB$1hC&w!%*X6d#y>cC2R>B*es zy}+nK-IK3&VM|c(y)7!B@fOdCH_t$Kq@Au(YD|lqb*8@T7SM27+k)GYdMf$_w=K9W zi6J?}fVMnXVP^i+c4ebzmcsQtqFUS6z5LI=`POL#;88;ip00i_>zopr09>~j Ay8r+H diff --git a/docs/04_cv32a6_design/images/mmu_in_out.png b/docs/04_cv32a6_design/images/mmu_in_out.png index ae98acbe596721816ecfe3674e75a2051f556a65..e9e6c143679ce7d89a75ad83dbe813cccedadeb6 100644 GIT binary patch literal 69538 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 literal 33165 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 CP@P-= diff --git a/docs/04_cv32a6_design/images/ptw_in_out.png b/docs/04_cv32a6_design/images/ptw_in_out.png index ab5aad7bbe3e7f428ede9d246466ad527d4a3a78..e8aa3d8986967cc0a892e301087cc3d65b094b28 100644 GIT binary patch literal 61994 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

k1;%y{fz@j3*az?#!yTD$ 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

99+qY1dtP1aHa-5@{G3xh_Q?r6*<#G|%#0 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 BF$(|y diff --git a/docs/04_cv32a6_design/images/sfence_vaddr_asid.png b/docs/04_cv32a6_design/images/sfence_vaddr_asid.png index 12c8363c1e4ecc34a59b57e7e2fb8e10ba502384..ae9102b725d5521ae641f1561e00fa7bbd90bf45 100644 GIT binary patch literal 52082 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 literal 32736 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 z z>zfA|38||{vuN+xcK=Zr+c!#57_ z#(pu^vWBZM3U_~Y_y$=^{n;~elc(PZP-BIV)a+L*QDt4j&wTre$ZbtEfjJNKHfUb1 z`1?1EodMjlugd&OwJjM&k2f=}u!59N04ZSxxHrUXD?1Tx0LkBE@AbjHP z?<6SkC47m3Vy@nSB*)zXH-6@1vO3_=(>E0n8+wMn2*tF7 zrZNpt$rD2d&zVbo2k9IceRyrykNxCR02|B7j#aB=8VfIT7L zcN|2%6U9Bc<9FAE!fx4eP(N8x(f&Qwp9CZ26331P)AcUzo`!nDPpw)wRQ&Eq5o107 zD&in0=4h?z5HmIfk(u+5)Fwmn?ke0dA}ZxLW6St;d-IhdyPN0nC5=Qi<@(hw9v%8C zlg%x7a<TabAnGm5~N=cM1U{?bZTK;d~BC@oI=>W(WWQc5oaoVez)QJ^kY0^IIe z4%uS&Fx4f|nJf9rePo^z4S=a%4>Y&fq3|BufPnPltGOp?z}4UD`D*A;C|7&FwK@61RrAjE}ZnCjzzW=m~};{WE2-VQ$ zHSS4!cGA9;3(w|ys=sXF4KW>IOLM{AmWUE`EGVvXaE1lJ$Pe+OO!`Pl!9^T?*p!@A zhTay8MetN@vCP9hU6it0gwWUSvzV8$UyJFm+W>CNVRs1TN<6iWEramG)$gT4++Msb zpj!!l$q#y^@;hDPO7O0rO)7#!@Tm{B1%nD3+djj#enOGG?R=S1rgRplR%5CQwVn?$ zSL}QZLf@_Ta{fAD5A=2Z?>W8OYV%tkkC?(stYx$T-$ zCnFU{m^kKgNvb@~XpB@&&y8L0CDJ|#`-wzH%kaRTR9y%!k6j_eP;ltP&Lm-vi7 z^cs=g0e6)pUsrPh<*)A;ee^1w!^nzD!ktv!&YQ5~QaqBw z^OPK$H=zbJV#$AzuDOr+6-0bqxoFa6{66DAtcx7Wo8a*cx&hW7lt>It*~l_cUDbZ6 zC<-4CqpCb78}1s9JVB(vRPyhly-4p;nkB>h_+ZFM`E)xhpGfjVv?4`RVE^4A6=9d5 z7naZZ!2uJ>A51&^o=vJF0K{A7wYeW+f&-%H^2Dt|rgAR2Dv=Qh9QJkLfXg=_oqtyH zfeA=|+QTapvuudL6jN8}>p%BGJW6E5)DxDA_DTv=_1HAo!Eqmyz==!e@iaFj4NFgn zUe^8<^&fZaJIXo3_j{q@Sa6`$7v!#CGp)}au0l-X|4@nf3@jao<}f6iqc^RJ>99Ex zFNz#~&PYKtjdt}`LyG>Zb?nkJTza)Y9yy<64#+I@Z=Rb#>JQY1dsp76A_M#0SudCR zirgT(INtSQE|kJ~{NM|hha*D_&W1E-HSZO>fYrdj1*_sC&=O&US z3N6U}n{Au_%!%JIKWUiO3>1gbq-(C}X2vGg?jHWS#5vKaVmQN@iPDJJK~gHzm+efd z>k-)G<3wUkwq<)(#4GdHHm3bP)Md(HUk&tfB!dCMP&~^+ZyA5OX!ETi%eEXDZCsLv#kirn3o zD=AswJKwvvc{D{#hb5gju4n-gP$ODUolT=#e!V0QkYOEW2TX#rFR|1uXw3=?BwINN=q!Yc2|Y!9m1m!i0G2Wn;Qs zNhE|XOPHghQ{&OGy|iL<^mW3-#rlX5IEp*BxP#C4R8lm~*PH)8poiZ7!7pf(^BlWZ zo6VmdPIIO0zL#`~T8&n9vB*p@iIb)3YI5B`Gm>~-hbh>k3B9DjqQB_Gyzr2D0v@n_ z{xrzMXuSrCkX&B?T!ld`syyZ|G(+4=pU&KK66v<>J4`RweA)Mj>xCp`98E-_EtNAS zH&H`OS5Hz+%_615qF}2_J>(nh{VxMWMKXOhhZPES`moo<2_@jByoPD%;8L`hEN|5@ zQhOOPf)Vc)_NryhrE7p?0-54H`Mx79@P7jq{$!2zEkAVK~+l#1b$4K8tdfiEiR?Wm)gX zJNMU)cjSTi-x`Hp6157js#JaLEwHpgJ|v=f&5j;`&C-ZBL-zthqc-tbZ`SHE)%8V& z)um9|<3}o^a3t`kP36|&Bxgt%F{no za-i- z8oZz=Jlgcr#EEYj$S`d|a~|N^l~%q-F%0;Dj1-LJ>m>6Gr}g(Oa8PVS)F8Nke=RF+ zj1Olb9Y*&8_{%=^Tw@-~Y=lC>O9Wj2;pKn+iC`iZKHx_L((oLPFD|G70QX#yRgS*@ zX^>*67o4K~dw{Xqn52=cYDKp(^d+*5)TGHXU`n%skat%zZJs*hPcC8_luXm@rxEV2se+i-p+ly|2f!j zRKRYQ)Ns^_f11scQ@SfA#X9_bOZ}*XpdhHXj8;Sm>dK0E)*e9Ic;+qg?ywoh%QW2}DF4N(Sc0%UU+q z<9JA8OiS!mvo@k3cMb{N8e;!ejUVNneuKj znF+LDWf>lbd`Oe3PoU=D7qO(ZKQ5cRh1<6Jy*TVw42uwb2k3_MB|(c!NI1qQn8&w%|XU{&C3lT8DCXadcW1h(v(T5YW3SHL90=?d}_aG>#E%OP`kIM+l`ZMwXUN$@Gkb$z&igckKcJI&I zv1jkK44~b2D;ac&`v*eg-Wza>5g%BOBs$cFE7z=-(PdcWy)i7_Dnu;XKx=oskzW}6+oKi1FRU|$#cCss*Le& zfY@ixrQnC>ya0cV(I>(Q5AX^T>=n4(&UT<~{!(Q=$wCg){1uWjzk<9+L3aHT3~>Cn zClv*kv&q?DHd~%5TEWBQ8=ifZ4@HnmkXL3%#Kw1x8rgxe)gMPA%F6+38A^`8dIQe{ zuq6IDp`g-#PWUYi%R7{Y`)4-TFQt~bYOF^%XW+q&3FSE@@y{^U(seTM@h72puS5Bv zn+Yu}I})E3$e6s>X9zrsYmr|5>uKkrnQgiwwMON#Oznh`DF>1kVdu#p?NfvmK@nPq zY`)Qv{S?3yObE?4DTb*E&N9N%t}G-4>}X)0{se}Z>+3>eY1^iuze(d>A0vV=z|!fh zZ0dBBR2C^Nl}YM(vLi$V5HNhZ5Wgp4u$_iXUAhh3%k>w*X&Gd-VE(IV7)!uet)tz? zjw!CZ*eVY6EztoMLkn|3j8JDfGK+c2{o!>Y*<>?&{Uf+o`NT-@3uKWuL z4Qvh%{q&gZX0lY>YsxiTwUCP4@z2h$CDy`+L_Df<>`D4cfO>K2G@GR8H)^YAL`DU} z2eTPhUAJDodLMkU6hxb=XkV+uMe>E#CA^a6$}w$xw)mGhH;-N2#phD-nS>rY;;>Y5 z2AG%>idI!??)fu0gs>WzebmKy8Vcy9k#zB)VxT7tmu+bsb+~}WxA%sK(tBTg-F%JX zCdak`>^IVad5JNu1Ir-cIg%;9dQ#Ug7V&ko@rB2 zZ!D0f+Rw$dgaL6bzNjXbD6an&tPqumhij0&B8G}q*tNo%#PGWE02Eyb0eYV=%`w4>c?JzZPxWdK7{CY~sH zv9jUpB>B7Gj}m!7YdWujl?*IoinF317 zDL4JMo*;tEUq25?$p* z-;XB+KK1)(sB=z3i>kj1cD|G%Z$CEG!ES1F4(!Dq#mnK=9vD2+Xy2LoX7jM-4g6+a z?9yziiG$z7@?3;JqtW20XZ+jF{X40}j!rh>d(N<%<4q^__A{GL#}eOWVV!5$Qh@M^ zi6&6ZQCVz$vYcwzyXJ4awSzz6(d0#~?hZO*_6|$p z&nBfq4qw6$W>=a*HP!#qJ&hAYXxNw7{*l?e{-#gG7opQOsVS7K5=BCQhzx|GOu>UT zMo2bU1&#Ci0hi=|Xl1(GnV{sqmTs6`brv4Kt`qgGF}^IK%)0)7aPoR!TF`_{=`b_W zg4T-AOYlTJ>lS~-zQ+h`Y(E?t%>ZRq>=cj-(8Cm z+0FRfWfmO*P80~9sZ{5fzb#6%e@7$BEVxH0Pq?q*Q;^7>8_2?7Z!b&R+60$*j4Rw7 z#oB&Wr75Ctt&uzy2`k5FVTIt!t~eE8tca9GH-#S|nRVSo;`B(p(>?)QuF^<&AMyF8 zg+dG0FPHi3yw-{BxQ1sfi~~b2l#37&SWB#1#|VTraDJKvhJ|kv{2b?1(&1=6`6NNk zbu?3Fd)&ABkr+pnDCPMZ&PuHJ(ew-!3sxf^=dZQ_sYUC2rw=0X`utpuNg2;r#RH;O ziUsC)vfA17k5mZMHz;IYH$*8$SX*HYX<20T+f6(u-8^WW-Ia9G2xHU<%-NtQCP_M^ zM{3b2DUq9X115Gu?$M3Pr9(>7(TtaGC`Dl+NJG0?gEK?VaU9kkUY?J*OF3<_uMLfK zwig`XXKkLs+I6~8>>c%2vuF>Ae#$EhAML9do0kK|XyxhQfAYn290$cvvcN}Ai7Pwq z6yE|3K ze-qLy@c{1qLUU;xxv;K@xwKZb(5HK~R1?PAp7o_wsh^rGtr^Ul zxy6aWLVsMZYcGsGVTse5H5LCIZSFHIO1GUZldey_-7$emLNyvhsu8bl%Ts%hXvDffN z?AxT4&bvmE1*h11!>N+~lz#}N(z6KWQ@R_5-|I6pd;zqP>};KKORZ~d1AnP%GF{R4 zN`udr#9k;==spayIFl5iLWleIgMzG!9rbqmOsTpPv%s;pW_*eRs-Q2C(XWtK?36*G z*WLJ7h`BpGWawQR%1RXUiJUQaag;0Tj#S#Q_J!iUh@G|xZOLSn%mMu4Qa-wtRWCs{ zA`9bY1%Q-fUM^eUeK${_n4)d7WbO@DdLfr_BApBuUP;h}qA=VN%klH-f*0-rg9>%a?q zHv$KQwGD}sAH-Q>xf<<>1FhkPSX9B=Sv6w8ZW|sqw}X2t6EU^CUOjn@Ji;Yu_t>)M zPOdi73~xlt;g6JL3L!ab@IEI?wp#I&Q(~- zhx_F&_my4#V%_=YATg!tFd`%mCxPG%!!Fw%x^F5{uz+s^UCfaZ;)Tyq*@f%KH23d^ z_d>9uCk|k{Kd>_gfSCJCKo8Y2Dh3SlOWQ9vt)}?laRF~+$i}(g5el$wKq%USDLm-} zPREXL@4JNh@p{U2(UXoZYNPjGdo*U8Q9$5g*SNx@8`x-lu9Ha-EBE_0pkqjTCj|3a zTq&mo!ommUWF{#C-13!{OcM8G8Kd&m;W0>VWoiGI6L$)SW-Wz$Fuml0xstY!VyySJK4vcU(i?kuAmljB)nQ2l`RXYEw4AE~%ensG$Ofy_ z=#J)Mw;Pfn?(0%q0hNl6cptJQoh{p8t^Me%D0FUyozH`a9(D{zH`RSNTs>Dq9=3vC zx^e;}MgZiHt)(7ULUxu7_V`8ww}%?|i-|CbFXqSM`PZb@PWRv6ec*!rl?rx+8NzC3d^sSul%x|a?@=S@Fj|j)pS^>S`>(kQ4z-(WI<<*wLypMil z0v2#}Th?U`Ko}5=)uwMI5TV2&#^~ZyKBupFneM-=gq)#BhAWR{$%`b*Y=aN?PfqAX zE}%-h{qc_?!Nd@5q&0sm*d>a#x*|XDpb?0*+^7(@Um{(ZcaH&mq0ekYk%s@3A)gmN z9j@iROBHINoe$`ul=8B)#?G$KhU0?AqL)6aTXM4sLZL$p35VQJU>Hp-1rvJ(h zfnC;^VhwiIjRre!EJwYS(+b-L4jG0|tDp}b0DO)2J4ue+vw$X;1hZ$*5kjJSq}yzs z?_RCFN8aA5{%}afH}oU@PCfm8c~Jm>qCu-$`&55AYolAr(2n`9Z`tRX{_zoUI7HT8 zv-ls;(0f^+QAP#rz3qgpKCeh+of=}Q;MeS8g1$`9f6+MVA=Z*fVVdWxp0VuxkA!^b zpR#7vstjCex@Hw2e8&@?37?*%967ofM=y!e_ACC63eh{|B##Rcl|(32GIxqsH7)5N8j1Eyz|Y~w74fe2C5Z0uIbR*e7qnn-ZcdCo)64n2Wo~Fp~Px@?1RxeXs=0S l5dMR8QBEgeD*=A{{|dP?RD{5fq3> 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 diff --git a/docs/04_cv32a6_design/images/sfence_x0_asid.png b/docs/04_cv32a6_design/images/sfence_x0_asid.png index 6324be4678dda78ed97ea869a822beedaf68c004..4dc0a4c861414f9e005e07b18ef938434378b5a8 100644 GIT binary patch literal 50819 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 jrvSL<#Y522Q1=dJ literal 30241 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 W)aU@rM^0@cU~`EK|Z9X(0if;uayQ9?IO-; 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 diff --git a/docs/04_cv32a6_design/images/sfence_x0_x0.png b/docs/04_cv32a6_design/images/sfence_x0_x0.png index 3c5c857665fa0d0f45f7103a1263badd919dd225..22abcc73177f11a4fef200aaf9783e7850561b9a 100644 GIT binary patch literal 26869 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 literal 16941 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 B9XbF2 diff --git a/docs/04_cv32a6_design/images/shared_tlb_in_out.png b/docs/04_cv32a6_design/images/shared_tlb_in_out.png index 3073acfae5de8053a19eba8fe1e0a6c68f066220..96b69f73820376c41867b8b6bede4e720750b865 100644 GIT binary patch literal 55618 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 WkjdN|85tDdM@8|4Ld{dl@c#oTql!8J literal 202926 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

I4$1X|698`0m@mu4d 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

q7C8!3 zP=~DW09t(weXw;U%;7IXAK2^5nk!**^{Q9e8rks1HwRlxoM6$juZVFs{i@9F>b}!G z&B91Ssu}w-^1G6HrzzCpMkX83=+&ZZ;~YKB@-$cs_ilO7`&ApF5Q(ApKqzt_pu}IZ zxEjDRE&`d79bn75!)zSux2idm#BF&EQ<)G$S+voktovwgjB>mJ@=AMv0bhBks#*mV z@Cyk}qceNivB~(xAAS)>G_YL;lUK>6Cq5>7P)^4aZ7O5KlryC z64dwcnw%bE1uy_Zj1%pBF_#luufycu&t<6URn32GH)RUnDJZ920fRJn^-ksL4sAs` zw)=KyY@ViZIuo@)4mn}sun)T)$n+9nke4e;`^Q?&(O6cO=wunU`E`J4o

{HS9$9 zePPZvHf+&wUT!gdXpOfJn&TUjutCZJ3Y>m5#MC#a%E9~UqjL5Ar9;=#eA>hJZ)Ud@ zVHIo1mL{SS@>H^$r^0z%N&M4_@YuJ!gMJ0F%%txeZ|z5LEbGz`qofS@L{V*UbLCcU zN{5;<>cf)xbo0~cubRu`x~2sS!OrLGEB>8vKOz&ZcP4ZvALbz$9BnPa*Z_1j*y6zM8!ab%GfElTAwE(!{KoX{;-*|KW zW;$%9V_Y&1j8)2M<9A+_9j$Ui2_S&ew1xCf>&m3Mn>& zq5YQZ^}ylJnjrGmM%olL__HWY_h$RC^Io44n1q?HVuUg*BJ6%e{;>JuW+2(OS_0%A zfn*=BD2=CQ{%;w_=u?cfNo3ti?rTZN&EjiMP>G8T&I><{(;2B zXn%nhX~fd^B>BG4T{GiQVmziJEwbG$Q9)kKICZO zLoQ%<{ZU6kLSuh+DL46(pP}bBZ|MOKoHYKzpJDt6$f7ENI{V~Q4KzH23R47%`?l>m z6@mg9R+)n+mUYNSdK=3{TJe_2P+gsuB%8m2L8@NFleAsi(BcAc%aapglh}h`!psuGf6uiXF@BdP5@!vNZ&+wT zrB|+xr0yNBr`IdeKLRCUB-Iz$JMD@l!w%6i%lgmT$+>tt zo+p6;r?t5{AXSs7T4*7VY+fV_0s1l!Xa^Hlrr#zC%3q@a&hOj`l9c{bS$Ks@qqboAJ3_d?$g=$bqCMlkU%d|vpra_h zr?HgzZKk7;eI?7ww?7Hbzx@V~a{dG^iPa$v(@#Rm8+5XfGehkXu7N4;XeS82DZtzI zjqPL^v(Ka54W6nV4g9@i$k>huk^`kbhMduZSU;1h)S_XCFD5e#AA?fHg_}|uYK+K6 zW2Au+t3FPKA|P2N{KRGbF_2zhUElwEOLxs>u-pwA8h7xC^KIY9@9tpF9cY||HjF@C z30`4(Pds;!Xuep?oOHvT0OrU^JGfV>OnWEJH5Xtw7drQ~SuVa~qpbbMV;MArahr3S zlR&Y^*ugPv={)(LG)sWJf@jvtuEGZ)U8W4ty;!Tbw`(2m=mF(rCRD%yE`bq<_#bwA zE4a7`lcK5!PNBXp34_;<0ys^1Brz782q%5u`lt=(OlAWupG(H{F z*k?f&4r(6cBBSuF&y3KKZN+Y6xoc%qWQ?AhUPDP+L0e#Bw^ARS6*(Wn!!AZ2E z3dq451lq&+xzx}FvU@~N(Od7|fB0C??~7hP*h|&5Lqh(HX<{39aoTw%(e+a>;SO-} zW%Oc+7uxz zU(0*5z7rQKCCZ?V#LiElY?%6MI>`<3SQPB$b1SlinJ&tW1USdK2_oNbBG0P6++3nV zI?xzMsSeap8};7c)!b9E6_G`48J~4v49_BvJUrn|){TvHW&^xnCS`AqY{}U@Bb8?f z*^bx0Y#2A+JmaIyg5t z#)Gk~d~IwOG+-j^wl3?;kx7RPMXTTKkiB2CzWU|vOXoWK!Ei0@_lc1|$3D-4k%dg* zUC498A34N28*%ha6Dx1vMy84b)R0%Q1CEebqsYE!b^S^BISc#zY1^?QSt{D!sXy>H z@Un)0tqQT;C?<<5YqS!$5dr6rK^H1moDvTlOx8YbA1 z&_zD`vA~h241DX>(D?4UL(KeoN~&tLA>>J7nxu9pt$aUjXc%tTXnds5kwzOkNjDE= zClnZWj?0gzSJI)kIPtiR_42*0-Ss}P{FQmu4a3&swI~^;ZLMotBGmPOUtbBrCYwKP zHC@OcsEAi8W@Zy=Yz20`S_38oMh~bC^@hpBz#?p-<9c$ILgXc>y+A_cBtM z9bqKsSSwwk`PRPR@r|d%>RhPsv)qSQh;ydpAa+tib@bH?`|OdR%8sViWpjaEdgSH{ z_|Zd{O<_R5@_361;|t>uAw^WZ=J!w`snYG@1;N+|Ebn1f7M%I(oY8%;p$G4Kj2d3j zR!RIGIRey1`%CNd8!`#FhCj8C2XFpd#5_lXL1@h#G^b@D!$HD0gxN5XVq@Rcv zipgb&qT^2vk4_SRw{l-V_T=e@$%j=+)XW5LNax9c?BX0G*eR(MNWWny23_4lhSIpT z4>e;r3BVbcR*-X!897A76TX78cw1Vq3gW#@Cz*KZFI^*uGDz`O@#A1OSnV$4R zL|z2NGw}q7AQbMPM6#q$##c}Kq=I}%JI5yA_9ZwaIu5-)E%&#oo{u~hXmp_0Sm2M4 zjyhnsz~+ZCA5L<=h!Pxy0y~tDf$M*B29bAP7g6fb_UfR^k7GpwQvW&og^(m0HZ^{9p~qBX`T?>sr4wbD1#v{mJw~MHD?f|O-arUUuC^JmJgaBS z2L%@O6zT?JHm?yJx2?XrV^Q@oGDhzJpLbqOTDgIhSP^_;Zj_p- zRNi_y5^$kkkrqu)rugt^to zj78r%KJOD+aql()XPUue+7z-gT1LoB@ z_D*t+vcekgdd-mf@>;Wtz_BS9gNkk8Bgldx`kt&Ob9zj^9M+XTw_vMs{9R|0q+7bdy zTGwuRo2OrM4x&Y)r1!-h(hlC~H|d=F5J1Te|o}F6^3hi%w2oE(hnWan3lx`P1vHH7?>0i`=m8 zFqsa}$6q+=ko{ZhTN-NnzdEz7mz-=Ov1v02ZEksA+a60?pGI!htqM#t@K2gQsx5Sn zFasuPMs{NV6)}bM>suxEGnpjy8Ad=y0TARK6(YL+iCUiHb#s|N9i&<@98EO`4C$wT6~z?k4FI@T34&S-K|7B2d9{oRE8HO7PWgizxr$HThGC8#^H ztcn|iC=my}&EeT%+Rx0?O!b?IsS}_X(QftDIZwjy5h1Dl&%v@x&ErXKI?VY-gtcL| zO}_bLgjU)J__6o!{v<64lU3dAX$JUpMT=}aoMZAL>Buimxa?IV&jj#YPkN&cmtT(e z9vCIrOzIpz%k`bzwugm{b%3^hL1oM#v{nx5Ub3?DZ|@%%e%&)2-FHJZf~2+C<;K12 zC?1v;L_;4ir`K)OR{i)TzGl?0D!=$A&@{&+VCF+m>ZK8FZCB-t-}qQV9)O*%rNslB zu>FZfC8Pnk_S*Ha%pC&Al;f5B#lA*67HS|NX6kBkCcFE&i*XE?3^K--4wWDODp*wX z?hz;?idGR|^|BZn$4Tb3df|M=bmVO=CS^&xX>~88ArLhBP198ZRz&Rft3W!P6;Wb~ zYXZOb_KPW^Qo@YlP06FkGW9zhxHkKvNW2%utQY9Pd8a5|JjYwkJdhe=2xi%JiQvbo z*7UJUp>}zywsPCATJ|986UK^_T^agsbZM^A8{m@IySJ*C%Qz_*%$*`Po;I?~3n|;G zMQWtPce&&b?Zs_~OdA~w6WU(v=kAity#g?iv1v{Ts<#nB!?&m{t{-}L+zEZzdT$J> zP6sf4i(o*lBRF{qNQ(pI{pbpRf2{57GP&Do-$@bq)uXdM;}!lxp^5*MC?_{`qE&K< z8H5u@yXz8DpEI|#)s^oIvz5(VWWG}6W(ltuTtDPg=S1K~K3(Y7K)QUVKyF6CLPlFL z+*6rQu{iA1r=X+g6AOsqcZqHPmf{Em+^&l*XymFULpx<${C+7LQ(l>dPDouMS9JXR#{P7vYTV7xco4z?9+FX>TW*E76Z84 z)U{7S<_F%>ITObQu~Sb!us_UUr)y!lkBh5iftiKjXNl!CYkUN)4Sc3>BLX z=opi^E&r_f2tu=g18u!`^O{nnZew90&2%Av7n-Y5VQ=|=2rFd2@wwcBMZ8f2zviQi zOh6I;FBTR>v4hr<+H>*_2Dc=RqCQqvl7@EAgnW7EYc9`x(ZyyLFtw?%Tg3?;wnv3V zVYJ7+PPVvQ2IP`WPO4x!=GK)1VPyNc_G97>Xov6p;H$^>a#zzTNTs$+=k^?R1Mj7i zMNC@a`o&HFB__4zPPF%$pm+f~VntwQ@19593?#b`EI|4jTQ?&>VNZQjute|&YH_L$W5J>U$>225rjZVY6oa?Gk?BT`9mM4a;IO$JVyI;qZ~m~P>f_)d%v~Y z@r&XNem~>KRu|g!$Q1Vuyqc9IZCU|*nn^Hyw`it>6~Vf2`lN5GSp7}ZAgl5Ilj<5E z&E%lSb6Fz>SYI*pqD{+?-o*34-VLCOiQBOVF8|jqS@~!JQn0y*GSB&UwqY)iHat%Y z(UT3UMVe=@5B^D7o!Udgy%4YuGr4p_gB@#ywu&t6Y>ecXkQZXt*CU~VW5@@XsPl=C zaE^o6Etu~!mCAL^D;Dh3o(}CyIyMMF@8IluJl^?o=b&q`jL*6?u!{=1D2TXhqS$V} z(KIq>YJ6A0(dyfQT?rucm_Qv<@r3HcP^ob)%Gx^KaHnT)6VL0w=Q_JjIPGoPoh!Px z-!iF??3V(tiDo$xer$_yTE{9$v7JxM*=Gs(IIlKD_Nk9w_#{nS__u)*P|=$MQcGw? zifZ}E4(U?~-^7H-RE_ldF8uwi4_qr*<-o4>f08fMxU<16_U}sy9BpNQKHj3RiGKni zY;+(Lh&8t(6~C0Pf)I6GY}k8>-0bGyZ$wG*k%P-<(oIc5M)bQe zndLLIh_68vENv?$RG1M4aU_a7-+l%*c!hy7fGQs=A3vedPoVfi-ylwJRjwIF+#1ByS1xAir)P8C z{(ONQ^x%~$$mTQfmZJ);*ws?gM$#9UfyOb#I231$yesK+P)NQ zWTk&jAN-0fo{f-(>1=!*op!*7nheNQd?(Yyd}`?+Cn_zi9P;|;?Gp{$8wiBl-Nv0r zzh%$$4~@PGuFpdbbVnQ~b)p^Z(0f;a+qK%AK3L1(mKa0cZtWf*=2z(W78Chd^mLm}%3B0!DxtTe(4y zX#rfz4hIuatf2-Om$mMK!Nx9;}o*FllBp0ei06)sWm(a z(xe>;)q77YW&yemx+c0PnNNy!_R+i#8L{@luu6L*%vDWoYhsaOP66vCBDIRU-Zm^o zNwkYp4ojZ)uGq^DE>B?@s~^%f@+7R&eeH+Fh!NMB) z=_ybj#-XG+sRRLGjor1)+oy^@ZJ_|0^CB1Wl0ocrJ0>5&-cuW=VsKA0#0iN=h*X4Y zHOB9kR73BFSs@f(H3OxVfn)y-n~sz>UpF>TYjPk3T-1xQFaO)sV{U;-5hnU;IZ@J5 zm;4QB9gyUexvJV6$6!-5FP)JzHLio~CJ`rR#Q&=^bxlg*_NakY`6# zy*SZ4M#wN;nWxjUq>PhiZ+?IqvS@f@uWBuuU2^TISqbkb10*pt1uW0t<%!qWEQ6${ zV~vdnSUi-425TmXuw>xL-++ZU-8fLE%=4}v3LeOQagojg^DLG~fTiba8Sd;<|57^@ zw>aDG6W=Gu81mujJ62#DJlpN%DWQ0)&X!B`_ZGj*o1=P~!p~NnvtxE} z9Sd!3;&uzx9pOr~xlW!gz&I$_CXuZG)^)^-Z)Op7$oNC5h4Q{J-B&xDMbkBHyzd{#|9y?``t!V$4~{7h}> zLPpuQn2qn;>aI}bR6a`{~#qiDo9=0wzaBW>)(5f5MWgZUHQ)(NnB9-tk!1ps^9t|F&i)yZ! z@~r#}UiiV0xP0~ae&TUXffP>r20k#t^sDO&`>!J;ns1i1!*O)i*KPr4=Sk~7kPg%Q zRQrON%yA^+k+pi3i_x;}GEQp3k9;$tWabs9gUu1k+PZFUW{pYdckPi56h{*e>*oT( zsI(5QdRznnc+?r%6$B^g+~Krds^v8*^E(_grp3m0*@aN*Te2@c!RQMH&K`FeHjLwE za1tvwusyrl=&F6GH(ltGpp>ndR z;6oQZJ05T10|sRC+%pGCSzvHZYTWW2U zvo77syjGVKvki333=8#OaX6RY*kEJ?!|;rWXTZcWOvZjpG2LcG6^Scn_!wc!qGKe` z*!)zpxCb|{!jav-__L@A;aR?%fb%2%X`Z4cn&2u zqr*D@Fj7cXYF!A5wK)w#AybZRU9 z)B9vT&;ZmU+I5*&9R-9j{!GAt2Mkb!Uvc6%cas z!F$|dfOr18^JfFyz4|uL8;ig!QujUe)N)eMmn(BaG#jYF?SRUWm19E&mDnb0g z?m36uhP9%6OIR$wg1HiPSu&sxR(Xa_Np8*<C4b45xQ- z^R0J+nANHYk|)u}K>r;dU1HL?S28%lGa|VKlvJ+T16fNSdP;hHS{BpsaEnJMLh!xK z9T&Wswc+@nnwabn8*7|}g*sb@^|p!r8QoC2goNlaxHU$0#L}s|FYlwSL}%ZhID2tV zoCzWi4SPrGqrnFw&pj5TI7RSnsv?T4qN-}Mn>Z;>)Yv_~)0>)fk0l&>t?WG1Y;;-r zw(~Gx@AHj}s*P9mp}9};T_lIar?aRlZ(ElY*`#=x{3^1F{3!5hiRMT0riW50FU4`j zE5cHZWizez`l9h~D@!g|`KEjVFk&oF@iYH+au8a4#T1xl66^jNsZ(wxcGofNxVJ1c z(K5XFAsbfLGYaSJZLgKI-g_<`yf!`xHKy$0$?G;`Gfq`%z3eyWo}He0az8Q;OIr&+=z#*EQh-@c+lydk0b-{r}?;Qe-qpLdKQ2F4*Y({i%&J>SMvR~oJm3hEq;W8wg;<8I$PwSRQwE}A0Qt3F@;do!a5{eJlJ6SCQ zT0%`CGrhA-TNGtx;&j!A$!r<2)k`VJD6%2x^VGXQ%uexdR-YDZBrX-!RpJ@)I@yKJ z?E-ceqva=bp7_TuM=w06Eu%=p@+5yiB_b|M+g6#6>Hli77^*cV%PNr8*Pc7wNMRt7 zEY~~MyxFYZ4kiClFXJM_vQkibv-Xf>YcVDrL0RV_ zmvK_i;+jibQtg?Qz^0v_EeXi&2SwL;M^d5}(w`Y#$r3&@+%133& zrGz9pC~PajyB{~;*x!4PND!oX?v_)pdtp|3Vj-qXQrK}BHA_urN7F!^wo$emIJY%1 z<}1)RkcPWm{9rL3U+3lKGh65EqIuB!X;QZN+O(9PQ z%v5IEt>R}>+{VvYj&?pKDUDqo*z)F4kxfOso%fMxG`=hIP=NWMIx5+ zr4t3R42ETw<3C5HG#_Ix!JN#E`RLM&y)n@` zMkUxH#Xb{_-jQ@legxoMC&ScPbR%9m4DZ3{HoiFCax3hQ*9TFri&77t%*iKgrwn?0 z-Co=1t~?jzr>L9mK}GBl1-s1SygNK;_Y}mTfKMkKi7};qNcL*!usa+_cgWX+5-Az- zg3sfVcU1_9(&?JywhVdd)#Y~3wqnJ*dc1)`94SsiVe2|v+0QHtpJfTwT*M8PqZ89o<2dIYEQei1uG zEY)rpbm~N&Qd+Rw8aO)p!fak}<(+j(J>{3?G3Gmq=a{}gPw2Ylmw0!V+wi)~CK{AF zSfx9`SSF{XXs*9QuI4eAax50auv>olb^Ex2^v@=!Ggila=lopXI~6n6O1}DfWLW;y zk;`WurF1IID41MZx$pe6QfvAm3c6V=HxxBt4`?|m!~EjKN-lP=!<~5u^_HpBZu_QQ zQPkLMoB(}{|48_Uh+xAYHxz6qep zY5eC#>e@a|8T>DQq3Gl>&bXSx)mZ%uF@uDzn=;5isiaO$*xH-tbcJ_~H94>pM#e7o;5gU6Kb$;X2+_FfJ0{5j7g0dJolHCoRy}ZFvWapbGf@ z_=V6;IkV*m^4mEA)B>qLmlj6l)UgTem-&fNlVT;LRgAmC)hbzWg-fy>Jl8x7I>=v( zmINz2Fin}t?<6)!T9{<4hgEVz&7@784aU{AExFgv8R!$zcuG8ycPgaZ0zF)Ze7KDT z!^4jptccIwf9J{9i)+eBej5#+K9p@=7bEX%V>IzSAjx@6h5&Mk?8jDv$9Bb)hk^jj zKGo8ArNOyTBRl92In#BD0}Ng~ai(-7c51S$KGyHBHKTHsZXlB>=bnr+_*Z>Bdfxbk zoPRMrfTFP!xfW7~XhV%;`@wDO=Kq!9;Dyh)gw19JB>COmRjCAJQB#v;y?y;y^e={u zb--T7ngw%r(*I9h&_oBtI!s}rqTL$4uE>OLP=ix=%x@ozMa3h0b@}@h&XFd!_IsBo zvb84Xp$AHbA^r@o5>F}Zu)UyiofPqL%o4G_t!n$cq-9Du4g0cdvjme8Vi>sArBsE~ znGyP4>w9k9p)6#Gzc3fWeJ>#$W67AO_Kd3Hz(}`UOP`$J{v*7%dZ8kdcFDcZ+Pr=i&SIgvA*HkE9f7eNvk%^7tdG*f|X+|4U2td?fR_WBLo!h3%;E}+nEtdP+s%%Fl#70lwwrH%7y?CHYlZU&K_0>;Ntj^- zR|&O>{N;B#1>iU2Dj5f9nd*-(8=1>%&^)up&&6UGY?e;~#)dF%xX6^FwswP?B0H7-P{);h@9r5vLFFsOtvPusu# z^D%#PB6?Y^8fCi_IHV^GEB-lSu>7jmk? ziz2)FaG=-Uandya?|cYmFw%cnm5sB3`k8D;xu+#Y1^IuvG)uv{KXdhGms={M_-7R_pkL3| zw^&f6-s6x&+#&MLy5Q93%HVT6O#ruYb1+vBlp?$2oai#y{tGT21@ux2C0E#g2n1Bo zK%SpdB%1HS*w~6(#lno!_&A&U_<@WW$AEv>-~QMwaDYu#4WMO4B1thk8FkBZ|DfzX zo|L=Qb^krIFf*X3%Tu0>e||6<80oJXjz1A_7*ei7n1@I`_t#Q7HAi-!;!7AnjECUq zK63CvhRMr^$g&Re1n*88s332B!XhN)HVPmPqH@^iKm_AufVO>ionhynJs<}QX7aDq z+$9s=bN^Xkx~PwDFhVcJleez?MIecURd-3IL)>}wpWws+hJK)L52gYHG;0Z`yuAuSU|5C3Ocw|3AwF1>1{dYSWv&gLJX^Yi5}Q=7 zBuxGCB=?=;*Nw9mB|2qlqVHP($$1KFGssX(#!jTGpqmA=MI1u3qyj8_z4 zBFz=C{(la^yJA0zQXGs&s%GRrjo+ZD)4UI|MMbL1LId(~KXs;P5m`T{nUX!wX8Eh~DEH{45Qj*SW8^WMm#I3< zl!E?4A-DlXWQm$l?F#a>s>ZW#p66L!xWCFiv&fw-{^JC&-7n3D$*6TS3-V62cHa)6 z&x?5DI(|>4sF}h6@{V|)fL^d%W6nE~RvKz}*gnAe9*=ORC#!ABX{YSXhub$jtK}i` zeq#v4@AuWG7T98G-5C6gI=V`z0%C8e@rz;Hf)s`q zi7burU5NDwx^McOvpn$G*rhzWk&!4P)ly@Te&*jHg>ruJ7+D_EWT%2Ia$Av)=g6B= zi&Q*>J7vPw#lJla(}|?X5FcQs`0%pxz9DsX7$87t0~YpI7wH@ZGA4f6a@Q&7;Ay+J z{GAi!o@866#jZ<{ZOy)XV`=>|+yf!8{SXdY>@u669{zs32C?-jVh|=o^ef0mM=zDf zP2klF5OPajP|H)ryfsZ+c`crL7Dr4!V8b8C0YE{o0~IUnAV>KZTfA`a4a%g(nuKiA zlat&VO^qkL0yk#zw~T&1qSmoh##hsvSS`f}@ZyU-qMA3w6TEa(qQS8u3g4Vs%ha5I zw^@O9@8~#7A^t|6lG{I1X=5V3HnSFzUd;Ue{$d@-#xF;B*@HyK(}x57#g}iHmzz^3 z-y=NWk$w$2jCr;SObR|gd{hXv)$z7)nevfgeB4rj*|ZbNp?hK4zQ9ny59LY*ul!9~ zHYaJ>bsPDe$U4bXpk?DO0EH_jn;J+`xVuY_;RYgA5;a6}2LtktLX&*lFc^8rn!_Vg zkiarkR($syoOELSw0;2f(Y~3k|2z?H($bB>2@zM=JhotPdX2gYB;ll9s+(d?7q4kW zw0XFjexeB_gUkzX|910pL-dzcQq#qWkr&?N$u~l7v!V{(s&fK#l8yHvkR$d2sA_*uMl<0x# z)thccDX&4KReu!B&&R^C>ZT@7_Q(G^=6vZ4b9)%BGqK4iC z5OK1QJFxbLD0mUzn}U}$PP?FCn3H$xdT(5>)^me!$y7=rRvb7_C*FCk6Gi-)eArK3 zeP3#zJ1y<8Fa(avKuD}Mp=N}Lz9Wo!^El?(yJCGJ1 zbD<5g(NrBv@{o@;Q@fUo9yv=9e11+9E)?zhRJf#hV-2zelAGJoF16&LSVw2Cc#I$3 zcM_TGiRHM`N5@oTD7-G+;MRISj?1HHzYNJ=sXyXY_5 zhDP8ZuReW(0R$QKY&;E_Y%3BuARBstSj_+OQrg576m(g7vB|>e7Z2Pra9Y6fzI4*$ z@M$_widSGW?O*ZC(kq;G^S;(PY4MW&=V?@s2Cd>sBs6th=xejK)OgFMDYwyY6M2ZB z#o12}qK|%Nx-M9ZeDU?Xuq4Ud3gg?@byjv|=Qzm}v+vSFQ#t*^N-Tmxx7ayXAUl{8 zQJdP4my8N{Vr=-UIsE8*?=VH1-va_VVz8YQRDXB-=;HDpCW4g>Is$L}_geAH) zCj^aL!reA;nCjnNO1TngqFBj0N%%r+p*NMiQ?Cp@R;73aLia;4to97WxGI2n#4U4; zzkE;MUEZEkWJCSU+(;Um|0_ z4p--N5x!#m9=mz&VY1)2$Rjt2I?u%(P!Q2)~4@hgsRe{q_#!&o0iPD@*cl^2G+KK$IX7~=tP zN#nN;iLfm(ORqhVq*CA9we+V)aVhUvhXS0Cq}$nm|3c4W-!a#+>pXte3aj)$EZbdb z_JeUXPJEj-cqZCIqkQeB0S_DDQn2#zMP#L=NJ&tke6Vh~Cug1oK({U3p=MHavl~zZ zTms&}b9mAxlA%x!zMSae=G;)MyRy}5Hkc6s;wlll7}s?qk|`8>b*9VYLdAF|3m+674=5);KEn)p@!#xRi@~=lChz42&4;j0;auho%+@<^DgrwZWwr(U`!2?;k zl$I1Hhvuo%#}f>H>9QdS+hiHto-0Ys8O8j_S~XTkdx;u_PyJJ%bX1y|JKQBHx%g~4^q-! z5LD!Uk)_||Ni3M5#M zfYTglbr0co05SX3AFQ8WVC|Bf7(XD86;ho-WumdK`iux6tU59yUlT60C#Rq+%*2B@ z@u3S4OwbekQ20dNq&X+=Gneh`oRvG@dpxbh!yZ}3waP@a-a&cLgtx2lR&j+k#j4W?W zMbhAE9I4eoBC_)?S5VqNO>+dSO#ZUjF_MJ=iQD5O*SM~YBIkS~@)~aXsk=GcXf`iT zg)>Z<>FF!`3&gDB6Ws~y73U(|W#LR_r9W<>debiU?3kJ3-;ckwY4C7@E6x<8WAwkR z^63r^4R;q1Dg433voZXQoHQ(%RKby;Xl3{s&K z?#vJF%GNMb8Xxg_qpt?q)c|f;^hIFM1wG`A7!DC-DiW#^()X{@1ePI~tNHW4m0SiZ zp`@Zj@48*a65w}>lTG&}Q5NGr;F2F)u3cj;tH9hr1ymj%eo-6AUv@u8xG*$+ln^-6 zck1_L_==U27^zLMacrqnx2Fw{PmVTEc1wAKcapW(dYUZnf(@qZEt(>`qMo0()cq`9 zsIlNfM2u^5^Q~hp2fY9hHzy{l=8IH`i%~!K*0BvWM1FlXUSY=+oM}AcIo@yC4((B} zI~Fm*O%Vr_*(V+;_Jf2P*qaBw5wCqaB)Y2#_qio*pqL^!7M^8Q27h#`{&#VuhHmLh zB@eRYE!GPmbAGL@JQnudNs!*fx_X0mMDX?|qIt1o(k&EI2F=T?AGpZk7l@IC&TTR% z_x&WYHh!H!7HTylHg#chLxMOYVuM@o0VRnKn49qd*zz3h#eD`y7$~%vQ@obfLIu6N zsAGt7W`^SyYR(Ja15|c-4&C?{zMUr7Xr_IYHU#n`$Lr$~cDVG%#mg=4ZVh*T}r5E(p;Tq`deTND>m(@EAT z+EOLdp16?1NeoN4GZDA6cAi&yvE>yCAvHc;5XqEs+hsWxn^&siOQ*osh`e9&+^xS( z_d?$Qp2t9H?F*MrXDLus>b`L^H%~TmI*EFafr%cYCWq}JeK#MH4`%yZCDCmUGADsZ z+u#3I|8wyPPq~oZ%qx^{PkYd7NS_SLRE0WI=&zQoEPaAdj{rnIYeaW~W~=aiB(GxO z6U-gGRJ^DYJB%pL*42BPfXA{Mg~<&#C1eD%@ep$wW579*9Np@8c)?qCsC8vx?4})z z!hvnMzqVxa-1No9ga`XmpP5}i^+pPQLZ}u9-z}9syx^4((~mDgc%0igpGOXy!_$NF z``KVpKISa==A8`+rXmGsMe*-y1{nCXa(nf%On}Uj(6$O@6uffrY7% z0I^IQpLU&)i?z#yvD|f%LDP79h!i0<3_jI%v8t}Sij31=XHzzu1dH8-|1RGD+oo4#0|2$ke6}4( zM&L?5>VM2Nm(NT*_Zf|e$Iwjn(6A0&QmRk z#k{8(-Zj`)%|MnQDsVsnm13s1zWcIJF1Q)qd>(G#! zO6|E^#$=?ARre>WDSBysam2jwH!+(G!=8UqPasA#4`A zLQ4=FSc9^ z)3jK6KNx}?C5Sw6n<>A6QbB)W(j;N5 zmlap>AX?7b7G4h(Wx?C=1Xv=RAJiPaPzaLsPbq0V{^fM}fv{@7tjF#xD}dx0CP#4| zmAirpdh?AP(r8|rI8BLiZ>Ew%5?EQ;3w|v5kGibTK60pUHMmR=f@I7-5e-YO{cbkU ztaeD|$y7R>ZH2Hv*@`cmetNF=hw?%{f+-R@Z6F+K&jdE4A--#jlFxv z4jWv{!}Z2AmV=&z&jp7Fwfd-;uV#a@5c}@iIQ&++FY3mOvllGC%WNfjbX#}6Z=(Ri zN=f$Ne{V6G9!EuFNger#SkNz7JEya%zVW8^Qc*9Tc(&#W2aJ-S|^B#s%Y1yk$k!;1^~9;^rrm=hk!~@n;7Pyy)o#H zig>H@6?rp_<~~jflA=N2Q1atOUq@_cAP4i)+s$&LmRiiJx?j@1RN)OXV$y&*Zrx*0 zC6pMZ9JQE@S&EALqfcn-zTXb=2&7ILA)%CnPemBeNYi&@5?mmm=z2&yd4T0UjFR~` z=%sPlWV(cA@2wmrtKY<;aFE7{!Oi0nRoSqsCF?!U$Nn_y~l1xF?7{n?@r}rHY2jB2ZWxLs%o19&ve=Q-FV+Sn&8d zjJPz#^SgJ!u0z_fnS@Q zOTF?-q1sFycCZ-t+_Q>Cyw>PM{Prz^-eboXbop~8oOSq8J0l0CY}ogz+HURTOOj#c zIAN!>SF%CSf>P^VpeZ&62;@LtQ2&5;8T71| zdZ3)^0vv;B#P7u8)qVU<#0{k7-EDqGhph3+GR%{8!}v7|L^_N{pl=S&$=yu8A_Eee zDN9J!{XmzDI$Ry3ILrp2q!d3WPpW`VudBbIV_VS!3Mz0#~XEVP`ZYwl~A zDIH_m;9|T}C-uNI2YbvVHK^*_Q7M`%{*6K#BT#WJbTi1HWX#L-?V02FJ1yvCFcC5x z+Wk@`l$40MVodwDZYW66dmX>ERGQdaIRhcK<Uqb#RF4HIK^|vf zerMk-AW!H3Q>-x;UGJRPbn7x}+`{r8;AfqPV+=4n=Y48sU_PL3IEo}?5 zWynUD{}4Ocdz0|`nzN4){CdPkre-_d0CQ~KZQYrviSX>2BTAv*QVE^^OI7<2xeJ(| zI9jzJvV>9U4gqrg$2X7w5v`Vi<|~kEL@i}T$N9a&NJf&zg$0n3r6Ir5pw>lXfuL&3 z{m-wk>(q>_>5a=T3j{p`#l z1rd|hG$1w&Fgv?TT2jhHm=dh?PUs1R-5mG-TJHRzPdX)>z z`X1y+6jcL>CM{>zx#PS1&m^Uqe=&ab+Z(-VoN-c?MgU^PX{!OhF8K5`nJwH=j%pXL zq=z#}*m!oUX0KWue~iTEtH%@%OuFh(+UTHIoS%9_gYI3>b1|I!FAaa33&eR>zF1`K zvQ?N=;cbt`p5J5MA#!;JN2X4qd`pK!?JE*QdqNka4}N2aX9WLdp^W*k7oIx-Dn6q* zN_S9EYKyMgM!Elza{q2p5AV@J?G!Zv#{$y2xQ8GT;g!H6yr-c0yBH7n&yv7y2y}V3 zQ(zTz3Orya0D)ZUzbBuNc%zxu&A&UFa1y;-N1@dFB)J^9+a54slLFBB-PAGR*k7@F z0sfk`L_M_E3bOBhN(v3I3T=*m(sJJu9#JmGKukF*93X{-61}&+-5E&$9@_`5p4%HP zj}#S*b|dgtAvF@@c_*;#4k3{Q zK3UXPV)|TO-@7d~N4fhg6$#nY+PwRs56Ei^>NKAumY}q|3uGn9_2I{I(Sjfm_qRvl zf3LBe06u^0nbY38@o>O?-TzG`Jw-OLK8kIRYyVMiBz+Oj43^|s8`HPBqAP;_IPkUslRr!Um zowwG2r#Zg8R9Pm z)O8ltq1$63&;Z$_oURNJE3wO^mK5-*m%C+q{{@Q>$3Hxa*5L-a$?Kt*a&SJT#TAO| zOI%;h{#yY9z(o)Rx#aF6#&KXi7A)El`{l(S)Z`n89&1cWY5jNhK7Y0SJr8M|aC) zyUzH*Ap50t|L)w;UCN9r0f@t*%{cbn8nAY9zix{- zt5*DMi7{{7kFJ~ZfB_7vfTK{lJ@$~=CigAku819?Sm(~OTmBx6T7KcG@0TNn17+V6 zR&oF8Oc20ce$Hd7BvS>f9;peUtD!(>j~-Q@si7b@e;4#5#5ta}<~DAf>dj|8pw;;{ zU>U*=lkYysa3%s!&e1M`<$$}s)cVQLhi|##53^eT2NB! zzSiKq{;y<~b%K^GE0Aw+pF*T(NsSSz=}4;GsY(D%(s?a7<;Fg4d6m;)8@~e2gIj@q zfCFfc(NNFq(-Ks%If2qU4o8>lvA6?#vR^-HytB_K=cAxvlfq{$1P@{QCGoFEa&LX4 zO$Yz_C#QcIiZv)z7={UY=~^|*n+W&n5*!&eWcrO6c#@AKzf zl#`p|)0^6>fW^_5hj{-2Wbz46034?h_I04<4ZyXS15fPoQ;Jk>q8R*0?4|04w<~*44<#a{Bv8{YF-hUh=*{osZz$bevHUYn%q+`!H z&8mptet;P3rj6=Clil_U5|uxT@?)r|B43MlTf5e1Hucn?+hHo@@% zoMY1V(m3*Il?IOi#w555F7q(sbm!unERXyv3*rF0=Qbg&0RP|uF_g+<=N7@M3(KZ$ z5KyI$Up=P!(Df>K9e1_G#lO_ba{%8=MI4&i#amS}U|*|r>B!7)eH1tx;2O$-pGeic z(DlN#t{9fDOG#F_xPl&ixQch*8-VaOhfl^}TesSd&72`2$FFK36u7T z!i02%@AAMU{J(cul2NYu-ff=8%ni85}$PmqU%OBODv%6 z`#SFNUo0B?0BCjAr^fDKaT|OZ=NNIDo^ym3X)yIG`>ok~Wy_I=z)O{fJ8`If&D10s zPgiUBKx^s`2?2T?@u4z^JaldFQvyR-)Ja-caMd~d!%L3$9KU8D4Eis9D%ms$@c&f( zW$-^X+5K0eXw?fKE;lafcFg}#hojYd%PTST8ueSx7nWpEu$bvp2~oZww+rwP9@0=Yj-js>4mHIA_Se7S|D%xq}W7(Q`K!&m4t*t_#EbAcTQsSxkO ze?m${64Eoey!%2*O%|ryCK&)uly{OKYC(K5W`p7%n3Le-ov>+5G^OVbzaA*X~K>l#Qq|2x`O$BMeNaD zO~M-#l2l7vQOoyITaU-6;M+ipYJy!g^GKv^_qXrh!U9&8i*Ow8*Gzd0H zF;9XUH+|x-+TH}#o$1JaQ}MnMbokYx5x2B%wwTu3RAA4KKC$xKS7T=5RBQQTecxOP z*<4Hq!~jmPM&*?I-?@M&y(O}3jWX<5*gVYRbDm|<1+FKQ$E)1T1K?%IYybHfK#Ua4 zKRo>(9Zsy#+znJ})bxTPkG$)0S?V|yY0K=>sa(`ji&9}h8D`>$kpiD}eT46tY^yn= zP*k`_ZVI<<>Hva}+87@LErn6%mq^Z zaQBrdPNOZats+rN5#)sh_IwjXDs#15fs`L$?v(JIl;geE2=uz_+fkSeh-u3gxjgHW z@gpp0?pGyhK=QpcRJ810L!KY72(?%C{i}V`cqeJ?87fDp(Z>&5;!2P5g_btJ-|Fn;z_tR%(=4bm=Y`P?bqnQhk@$H2jRRvN_Pu1ZLn7%wu_IE2pOChBEE~ zkqQNpTdz>tJdKM<*a^u+Hq=pl_t_4CoRVK76UQ<(XJXWsB2oQ6orZr%3efH3W5PTh zE;pkJ5q0v{3C-9E*hS(p3YsU~-0W0fyirfL_+TOG1CKNf&f6!ml8e~T_R~%aW|}g; z80ayUIApLOy%mH3muHB3hjT4(cQ0w@6!$N4@(N6lQat$ccp-v-l0YhnhAibt@VRl;B7i2dHiBHc}Y6RlX)ywn9YuDhJ-yZY=5Z18shL+5r!_VdI_+mb8^*H4B zx%+9eoNm9vuDtbAXDEg`uX};xCoJU*Ru+c5z78mF`+g`G;gJ^zMIwf0#`0HG3(gpr z`|fOpoy^)6xgr#?EPRw)o1P(o{I7=i?}iRx6`-E(gzG9QlwEJT4(Wp>;8^Z4c_hCa zq3Oy(lo~SF3=3OI`n<0Tl7-9gthL+^RbdR{#_4pvrsKK!{t zy)Y6N<#_Ge4W2b}Mafyk)}^OuikgJhC&i;{uh(u9&_9?KVQg1vj4<~Sx;|0ICM8se zx6I;`i53A>3P#!}(au73c9~nKlJ&<$t(%*+1-BpK81h3GUps#JO|vF8#g8M>%7#6k z5;n5gaQ0&KD#YBoD`#vX@Vmr%BV#W!C&&XUv*(Y-*8z#J|7_gKj^}03*#4Uya*k5f~xWQoRNxn`U3HXiOi| zk0l+;-fs8YxTV3bMWPTNkr%I*JRJ*TuuO}U*SE@DhU_$aQk^vH=X5#j$MU?>3;El49|t-|x+;Y-_cnnB}t!%fi`D^{Mn^wsynW|1yo8 zq-5dqIpp4k84J>8lV#4FzQHf3nG*_s%##pABts}8_MKt>q=VCk%1rM!UEWRq5Ln`!V@EeX2N*}v| zp(U9eHk<@>2Q$qRsEkhWnrf{w6I)Ab{!{G>yDXWcTY3s(Fa|MC1*hdP_c_zj z_ygLIkGpQv$^M)j&^_ly3p%C}YZcO3cfcmiZm3#uOFH%wRDX0J#W&>K;jo}72}@?( zhVb!%C??&Wmf6rs=2yrzho)ecIt}*2G?GkPQ1yiN9kun_3^ql%4aSSQOl1VOsm*g! z;lQA5C`)T?4Bz&~89T=rz|>cL{;a6+SFD!ff_^LDi%TknsAHG$k4v|_?yQ=V?X-}i zgM2JAt}S|GZM9;}e&T8w(ki7FfB!}$mrF1!|4&yPEh{R5Usv`{h67Xrwmp@ZLg~6S(oFC&KEuP6daoTJ<=6~vUgHi zsdz6S8z*EF@Xb;{Fv;rD@G8@6?^F}{Ww9{Lh>j06SRoqo`TN}}mJvUMEV(n?pYd20 zfB4ydqG?%0ag)9Du2qtBR;$-@cXeaUm1LE(kx$1fj18N)3Mj9+xOJL)5A+$~S)8pq z-=uAM^U7#{knCisH07hTdP%UD$J+`mz>um1q4zAalAXftZ99Y`Uu+xx@pxXDkx?%J@yNO!ufCtVYXB~!{dn};m{;O= z=OoRuSx`HC7nAY-PI9gS~@d3AyYga-Lza{fk z7mL7^4p-}9m4;tx8>S`6X(}INnuV5pq-SZVj6RpXh^G|ZVE;{rqKjEVsrxVV3+mLe zBXB1c&bgI;aL;DwTu7C1x`7{|`TY>S_33E_(HlknmMc@F>514#U%}M z_pWtMOl_lFs$0gn4o`jeR{7%F*vVl-4JPjQxLDEWH?#Pj5~qb|M5e@6Jj<+yt2&hH z43ac(bm|+=N-m^qD1J`>#D=$KCtq6qe6?6j(Zf@HvC#{UjFSFj@iHG*jF@2qCkC9B zNQ30szbtKB@Ms*+U<#eG>lU5aw)O4mn1=byO0#C+ZlXskeA<%*$9<7N#Kba@nFhYr zCVdJ*1q$SyTp!Q;OQ`XZQuf1SUw!rrG)Mt!w-2$jlx*JaU`}=$zU^9_aLqC>{270} zcUR+`s+@M>Wj1U&{oY*^Z9=zHq(_<`7W`5CMoq8r3R=@;{H_>U0-I|o zxf*LUsh92i;M|I>b4iw*S5txw?&|_&VKcs4|Lm*_=aZ(LX(<2f7oW$!rWs#Ck?SR0 zt=P*@oDQu^iEPqFuav-KrS8p}TG_p6b@K*ATRZ}KU7tQDZ&iR0f|u2iMP)_ZMihNF z3_>fvyumq@`Ftcs`I?DLrC;2$EPGq}c~2Ofj*eSnv&3TAFA=}?o+hDjq_;0RnK+i6 z#p?oZ|D650*`U-2>0p}TWGNxte@kbv?DvVJ8?*0xK0HGA6l}-Ij|kC~cXDyltXN0p z@Q>pHv8!w!1ZA^-hcC^SY~>rh)AFhOliyi74mMUWdv^XuserVrj%pIRw{enH8Iajj zX-ksTO@kS|2)g~0Idm9nPZt>8OvOEptcB2!-x#+;FJ3qdoxA(=2rku`N#{}H%!1(& z_qnZnl)kQDqR^BnOHOJ1B8^Yv;@D@(%p8j=wbg1L3$vLbn-Nt=OcsBi6=T)?%VKC= zm!mZji~2@zsB+9Zo2(0EfvLgO}kR%z0`UHy8XRA)G9o1+i>R9N;Mgo&Umk2 zvv!WDKG`5%x9bdTZTJ05Eac7<7v0@hBiCH9uv)h`bU?Ex{Zwk%N1m{-cc48ZH1?;C zrJT^zgs(rY$_~3}u<$VLVpynF;WUefMOx)aQ>u4}Qipp^+XV5g867?~>`jA+4wm96 zF+WQ`Ij5@X&-lq?Q0ctcZuYNCpZ=eF;P+#hI5{l#SWc|e!jCsyibl%s`?MDbB;{o+l?;v zO5rMr56mL;l3V?>i;7^qbuuFlXfo(u=RTl^D3yJM_@Or|bX#(<=D1?fn~U4VOf!Om zT?t;M8421S%|LBdziYwe*^bcb8i#sNa<10`_TY8rW zMKxbe6-sDMkbKARBd|Z*piZF21sfhpXToZx+V7H$D5;$c^?Fru%Dm;(JpKV+qY}6_ z*(g>e+iEsuw0S3Ioc2Omja8~p>(Z2rhS5keGX7WsxG3>aNTbnt>moD#KCd$&t8AYx zv$nEm!BV;&I;Dl$7qXGy{CFaTOvniQI)b=3g5d$fDpf)oSm`lf)0kO z=T>@5;>S#IX1)2Q93Sbfn~!m(;Ws@jKB&}4v-}TRBoC@7fnHRRU(J?ko z@UZfL7@5)IS_PQ~jZ4?4+K6dcQvX(VQOMY^8bR!Ko&7gA$9==+mwe^_F^{-~M7A zbJhy+nA;XbxvSQ#DM<_ZoK|$Mi&lAK6-G@t(`uRzOQd`yW}Dg6`gt==D0pQuFHMSv znIhTEv9C?Eq5C8_Dk@t9-4HfABneq_UKbRs4%>icKz1VN%1cU%C&FX&ua-UL$t}N` zC8I1EGg@J402NBG_);*V|7I~EC;@m;Jpl$xFIHdx`zf%oX#A4lYd67?$$70=RI^4Y zt$D1VOI=pek@avN=+fshI0M~mn)e%I!}1C`NXM@-&TM?7^4=@tWPaQ9bmRXM1XG$? zV9FD|I`~G|!s{K-%Jcwg6yMoR%BF3!u^h&Y+K3oUu2;(o22IfX6pN|D-!piq_o6`c zTkSGxls3|8M%1k=?nqx)bM;Lzqs}sn(LEQT^OpBi78QK*&()r=e5fO~j>3#U*zV@f zmVVl7(Tbrrx@-gDgnu6`RJB*Y3qb|E8qLX z%wPH?pT~=2b38%D)6A6FvtmRfB?OtbVIxv%%hVGW;Ih7QC5B-HvfyhPY2R?BMKTI~ z_|JsrPBw7=JTsIgxVGXKJcui(mI9cqTY~7!^taEENumYNs(R;yO6o1Lt#V?)+wBol z1SGLmDJ;1C*CY10$qg5~nykU^l<7L0xyIYe8cm>jwr`b|(rC@c88JowZ z7YZKFd~sb29vJsnzREwNQ>=zC{BHl1D_rRPYvi|iO2_`icWQiZPn7=ZY{b6ofxQ!A zfcbZ<)S&V2a&LUeKR-cmBQD}Joh%YWk3*YQPsj{dqW#O3;wB1LnOo%}dZ*roA+yyH zvZ*}8Oau0F57mK#GHl3yzECjDvgK23)uMMv)Ue?dW@|mZH8~+sVhieH_&E8UKO)~A z8}R>f|NV3PgoDD7cIektwzmRs;_MNc0_)2BoCHI|nv5eI3xU45gaQu$xXL%a932X7 zzKWY$ZMDE%CP!7Gf7t=DrRwgW)U9Sb=zTYKWS`7kH%JyVQ-_Idl$UF8R{SASk!z=k zC6G3k?bUluk=j+}4h^yIR)5rio7{=1kqlkLWjfO{qstJjNF|upJ#)|CJ6$6BC3Anw z;f9Ju@1cPzP5ootnxLkzO!WxX92=K|Z8J0ZU~6;^E#ul)1KN0rZ1Pv_zL)&2;Xa|* zYuVf_I#sAo`wMj?c0Czg@oTNVau>3r_xMJ2MTT= zfg3xVf-HhzjVGMb+EdeFaoLN_EZ41%s#zXeSO1QL@s)W`ipw~Z@=p;41gyL3fLM98 zBeSQb40==u&1a>cE_I8B#s*$xZnXbjdsiM0<@Wy(Mbs5pLzY2h451<=Eo2L~vJ4X= z%Op#-No5;K5=m1+W-LWzmys-CZZ2Xhk>%PcYf7>WA;0s?!oBzRyRYy4_xJnt=kc8L z%;R%D%Q@$Hzu!;ISmm^>+|*U7%C*es80;G<8%_XxO`Ib(A@8hSE z^2E)>Oa#_fxuZ+s^VDJttXK_o(F8yHbZb+|Py!_?O;-An()io0fVBYosxzyt|MhFp z^U!t^d3!}U4g1ElsYjoo!mv^Oy==n?z+{Yf(Hdh|x;?vz;ma5oEg1mQwmtkY>U>q{#&_};XUlS24*Xv0JeGOI*c z$uW{NyG&t^n6*-phwEhbisOW@WbsgFd<^pP~5nDmIOc zF8klt*e^I!sJF4$XQ)=Z-6!4o23(x+PIk1z*9#jLAcVMQH%T;W23af)iz9yugT*w^3<9g{2Pw*n$ybwo2Wn`on3 zpzJHIMWAQjH&FJKNKWjXNn47?PqupYp~(3x`1$g0YLhX;e16R(4(eB3%{pIY8UkTY zcf_La>G?`uL$BQ=xl)s{(HNUgzT@0O@URFu{th5~noY!1w$4#M3E!>|84OVHP&4UnJ(?dv1M@0rv{PEeDW*CMK{h1JfrAOm)c?w_lr(V+jUSs9?3V##^_zc zHb?`-iHN{PPOsWloj%zJcfoHc6_qX%($`3TYf7SLY@5&1ix(R&d$)JD){8_n?uuw{ zcWL-YaGp3#6zn4syJ1-mf8W5GnR;P>o0e*!cR<2GB&wZ^i-x9=)0L;KyfO@ac9q&7Wppnjivz4iSqMG z&xJ1qSBqRoFTNB@=`xGw*cj_$q@#4#dt5t~7xfCs<0WURhH*-LY@ZN>=)OGa5WSRP z=4m*(w_B|{*X2@*h2u(ZkMAcbx2-^Jc1dr=w=k;zc*WgfjmmKreMIg!K3q$)IS}DI zu`!6aVn3+5593_um~>c0LE+Qf`$Wtl9=wyOg!M{Uzy5MAe+siTJwS!hRZk^>t#(%Z z{(zk*#QV;WQz2MXfB(|mU;1C33Ru-ue;hPvANSDdW%78%k*@ml=eApwQFn790dDfw zVej>AkPOJ|s+;Ak>Eb^hz&1!g2>+Yd?Zesdtwj$kY;GcK)rHXh&0qSHo4!=xdsO8g zk-*0z3gVYI$_|SRMapuY#yYW9#}1?i$Z%>*Iy%f;Essd;l`TfaDCq@g5S>I0Yqx}n zwtHUglByjA7RH`&+i}gT*vMLqJ7m5_gabPQKY)3<@TUFz6QvCA-^d|nZBA3tq#7_m z`roEoWryI8HS8;04ra=hWl=+?&vdixdo$YiF}(AjAacgW7MoMVLr{%=+-rhY{qo4Q zDXZVg+9@&AGM%}LiPP>uVQM`^wphs%kf2j|0b>6RbGD+!}Oewkt1oy(uawWK%TE6 z*D{h6ZPs?*Q{0czapF%%GAJqN0{HJ5+AR1EMEcaQGg;C@E?hM@wLhBTym90t?X#Y}mA2X^hEP}R zN*$TDwRPt7>ru+M^9$Ag_2hJqT5x3YVG-yGqrCq)=lL z`mHf=Gp;Ry-sJ*_Z2MIe!0qKB$l~X3UaV6?I#p1#yzTT6-Ep^-8VmAx$IFccQHm^< zhdis}YIIAZVG)rLzyD!>M6&k0Q-RWrnbCZw;^Joss=4lo{!`V_;f_a(wrkq7G^k)u zlka4;R&0tcnH}N}T!p(YG%1h*4j0=U55&CB4{}-^i*AmI*xmS{;OzikL^JC93|Gpk z8UHI#v{fIV5*zz36&FW7rg)c}MZ<+GsR#|qxZ;)_R>P4APyFK3RyfNZ=mf3u;Z{2y zCxl*G8dtY|KJQ!HFF?JekP>#*=22VFC7(HkhBpR#OEJWzxC#@)f599{;1A^9oxW?B zX#IIA?G?wK*ys6rL)=qmVO~fTTeZL!8)K@JS8oOt6!JV%Z$t_^1EQJ7jN?-!`+yXb zEKL1=v@>AAzbz58m~{AS|kk}}k-$=LYcxSg85FC(X3DBA;>=omke&dk+UWA~jD6-oV4 ziRpD#bse4}zb4gKy0^q1N=Fad7;9Mso+PBl#mW|-KYUK@fmd7gcou6`Bq40a_(Dgy zHmocn@O>n5blEBDe6qWbdaGl?+H{5* z*rp@_=e=oj(>%s$4tyz3hkAH+XGIM*#KTO`{F9|K5Rx-js=2AZw$dW2L1!~E##iVk zsqZ2Fb3>8h$Cs*GCdIEsS^m+P;mBwEwxoly<08U-r6@Fqgx5m;K!Wf)g{lSP zQFXa>1SI;`@w(2i#vdMw$6}30!cKnIc%w5$Jh?JtlZT<2m}{7(xnT*Lp0*?^#>?Rjb)=nlFq~auu~2*}c-$ zG5x7dc4TCL;7Ov{_6FHMG8*q5Igo(zPzl=sYg+oOwsQEqRP*Cl$^cUPTq{_~Ti_j< zHNS>A1})LddoO;;eKD4opb=klAnFBftlTx1t0FmX*VmNl(KO@?w#0u^o1kP4-LI2ffmdKqAaW`!D)JR7r2H zJ+YbIH^cp2tJ^<5OL{jTVKEs$%;$xe8$Rvauh=#?5KEFL3>Kc^jjZn;<{`fP79rs8 zJN~=%rlV6BDZi(CuDgZG>iW&@KPu@N86fiYbKW8D3!E4@r{$0;C0mxL_ zM$6&&!n+Q_e81yR4)5=#R6g%oeT+r+`KWLXmmu2{rB-9lhFy2(?$B|oh-ke1t+X@j z4z^m6tO@py-%Y=^WMF!bZi4+pkqg_}o%$NZW_^7vSYO{Qiqr^OEzg(is#jRWF>MgLny9HGoHl{OP*1_c~-H96%@^=_Pyc(-tjqf7Z2n!e11$%Tq+?GUnhM z3k!>os{V0cMc|M!RSP!MzqY6+QS!QDhpv@Nz2ivTR3&~g0ap+=7qa)ad~+rCM6qj} z^D0ORtVtp{=|eu!GCi{<3}Oj|fUx`*ETLaGXO9nD#G^#rdA#MSu2)-L7}^JZ#RZ@9 zAg>?}OMdH$bjPwS-HFri#p>Hy^$u4gq~snZz?~212XKSc%LaDh?zL@)b~^A?gF8Hd zG!Q;n44Oiz+n-t4R<^}uF0|}SnU5cN>tU#m(KlFaEV>Pgu==Bbb5{0;Pv@K|0Q!Q5 zU#$s^%#tZ_&8QS+&)OO-%z$VEL;V$7ZVF))`JRNBKsKzC_ORENmqo1TwwU98oPC&Z zg>;m?=ek=>BGu28H$MvH2|{cg*a+q(4r0RY*5S6LWAHA5$7bqzDr*JT=v{Nk2inW- z>g7AKID;$rAeNDi3!PVFS&SWGhTdRS-iCJ8`}+)z}@ zEo`FF+zM6K)(A)*>-gpW5n)9mAO-95s_V6!)4W!an|jh$jH1jrs{>?wpHcTAD-T6H zgd{tqcEOn_X#mlO_-Y2QDS9A{Krn}O;iBLM$-IhGqHfnGPyFSWp;b3faPP6b`3I3& zZFCN>)QBi@p79zu?nv;9{JhinlZIVoM zd7mzu%0tqhJH5>r(EviW3dHIjx$Wxo*;j=sOl7jMkKrAmK>l8ScREORnM+K{ITt3w z&Fb=FFXlh}st|x8z7$r{rH$1_vPV4tdYtwNJ>M%5GUk? zq39I4HV_cm)pwt*2aGeYkU<3J4_clg6QF?E{pfukH(Ptb$1p1Gk=_9uuK6ZCo^|E} zKeRecK@ZYNUPcgoVd&U?>#%KkPCKmj)A?`r01Oes)HVfjH;|y<>kqZ=v;>w@-YXTUWwOL=J16y88kxBZr6UB`D_b{GXn6!mlEGMjK(vw%Xs1C*gNq@0j^B+=hHx>uoIMAK(%gAr z2n6Eq6~v}0wZZ{OQWs{)U=9F;_;$ce$&A(>py%#-h&lnUtYgwMcO0c@Qx}|}7Ii&2 zT6EhTq~Ptm|DJ8ag#vPbGOpk3@t0#~7o%LH&{*~=<+YNo3?0~c-;(yD`FQYYkl|xQ zMV6WP_vCO83b|Sm*HFX~v@qazu~&PnkA>6|&O}L+(_MEQ@|e54KbWS8<`>SVw#gr( zX^M}@0AW^v|5#WUNPNqCWZq7TO~B%3%x3}pR|j{9Nf-d+Nl4Ut4Ilnj;Sv_Mgi}Bf zJoEZ-njm=OW>lMB@L0p3b`Z5kuVjptB^w0lkI&q4Rnyg+rE!+^j_}J}4-bWbvOF9-th= zmg!(Cj&SzXKh9-w55O*#o1`@V1TY0JXshfPDd=z93!HWkq~m??bW0*WYN9EoIv#lE zKPq|u_arG4tV7C;+MLE#1GL250FRKU$sklh0U!#T!Bd;D@Tr9IN|d51Gq1e+CD=s| z$XTAH%O{H0P!EI9y7ahy9k{j`qR4u9c+e@bau9HBTFPMqJ+{D?YsT;aj)Qe+Y{aWC zje%}GGlpn$B?IC}R79N~XJlPy+Maum7&_SGAuny1ku-q>9FV{x^4`d-=)~ie2qo!Q6E=YP4z&Q4?n~gAL4oGkVjwCbBfxt@b zw2%*r%sg&@-MmJz~MwA;*<|DdH0iDkCtd(@%hs>1+5s%mP z`I8}pxIFXA0^q5Ba)lV&*8e+PA^N%=ij1+lpzsC34PC+uw2yD9nuH0aC>OTcX@b#r)@ijVY2hQ_!pD-m^YCvFf;Q z3%jZf7Z<;zHjagrjeVY-$I`D5L2qt&c!2&_PTn|vWpo3w4+B}TMyJeGa*PLkSaV=`_ZW}J^UMp9c+2~DoE zfJs74F+pXtIKspSx*ND653ZO=^D;W3O~|;Hm7iNycG|4mERuyMc}baK$2Ao7wR&7?+q$p*6|4;VraI;O-;95Z(_F+ZcA4 zX4{zMufxU%nq)gLWN`X~Jl!^4;0pNYH?!kU12Qc3(||uc@Mj1^7WkP18CBzF5&7R+ zM5cVnb!h^(*6O!4Ip|XNn@MP)Kqj#To=D1W7m}e@L@i!rbO3HrhF)-4+3Z+X%R(vJ z?m%O^K-caSy_0m0=;2dF_kJ?8qUQ-5xe&M7R~Xt0Rn}k($MP*L>ue=mdRKo*H2FJA z?@6cvLpOTE_QQ`mRz?(B2o!*>g-iFM=nq#Uizb(}#TGCtyc_s=H||msXjs$cg>3a{ diff --git a/docs/04_cv32a6_design/source/cv32a6_execute.rst b/docs/04_cv32a6_design/source/cv32a6_execute.rst index ea76f58485..f252ba7d4d 100644 --- a/docs/04_cv32a6_design/source/cv32a6_execute.rst +++ b/docs/04_cv32a6_design/source/cv32a6_execute.rst @@ -225,7 +225,7 @@ The MMU maintains interfaces with the instruction cache (ICache) and the load-st - logic [HYP_EXT:0] - Indicates that Instruction is a hypervisor load store instruction - * - ``satp_ppn_I`` + * - ``satp_ppn_i`` - in - CSR RegFile - logic [HYP_EXT*2:0][riscv::PPNW-1:0] @@ -1016,7 +1016,7 @@ The input and output signals of the shared TLB are shown in the following two fi - logic - Signal indicating a shared TLB hit - * - ``shared_tlb_vadd_o`` + * - ``shared_tlb_vaddr_o`` - out - PTW - logic [riscv::VLEN-1:0] From 066cde118cd1776c2428c0e2159b2db3afa3e03e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Fri, 16 Feb 2024 20:20:35 +0100 Subject: [PATCH 088/182] fix integration of hypervisor extension --- core/mmu_unify/cva6_mmu.sv | 1 + core/mmu_unify/cva6_shared_tlb.sv | 61 ++++++++++++++++--------------- core/mmu_unify/cva6_tlb.sv | 58 ++++++++++++++++------------- 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 38dbf519e3..9aa149666d 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -120,6 +120,7 @@ module cva6_mmu import ariane_pkg::*; #( 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; // + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled pte_cva6_t [HYP_EXT:0] content; } ; diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 98da55efe6..bbb3b8600e 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -143,7 +143,7 @@ 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; -int i_req; +int i_req_d,i_req_q; // replacement strategy logic [SHARED_TLB_WAYS-1:0] way_valid; @@ -160,7 +160,7 @@ assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; assign itlb_req_o = itlb_req_q; -assign i_req = itlb_req_q ? 1 : 0; +// assign i_req = itlb_req_q ? 1 : 0; genvar i,x; generate @@ -169,14 +169,14 @@ genvar i,x; for (x=0; x < PT_LEVELS; x++) begin assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[i_req][HYP_EXT:0])): - ((&v_st_enbl_i[i_req][HYP_EXT:0]) ? + &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[i_req_q][HYP_EXT:0])): + ((&v_st_enbl_i[i_req_q][HYP_EXT:0]) ? ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): - shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[i_req][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[i_req][HYP_EXT])); + shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[i_req_q][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[i_req_q][HYP_EXT])); //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[i_req][0]) ? // + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[i_req_q][0]) ? // vpn_q[x] == shared_tag_rd[i].vpn[x] && vpn_q[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // vpn_q[x] == shared_tag_rd[i].vpn[x]; @@ -190,18 +190,18 @@ endgenerate genvar w; generate for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = (v_st_enbl_i[1][0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + assign vpn_d[w] = ((|v_st_enbl_i[1][HYP_EXT:0]) && 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)] : // - ((v_st_enbl_i[0][0] & dtlb_access_i & ~dtlb_hit_i)? // + (((|v_st_enbl_i[0][HYP_EXT:0]) && 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 if(HYP_EXT==1) //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = (v_st_enbl_i[1][0] & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) ? // + assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = ((|v_st_enbl_i[1][HYP_EXT:0]) && itlb_access_i && ~itlb_hit_i && ~dtlb_access_i) ? // itlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : // - ((v_st_enbl_i[0][0] & dtlb_access_i & ~dtlb_hit_i)? // + (((|v_st_enbl_i[0][HYP_EXT:0]) && dtlb_access_i && ~dtlb_hit_i)? // dtlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : vpn_q[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]); /////////////////////////////////////////////////////// @@ -224,6 +224,7 @@ genvar w; tag_rd_addr = '0; pte_rd_addr = '0; + i_req_d = i_req_q; // if we got an ITLB miss if ((|v_st_enbl_i[1][HYP_EXT:0]) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin @@ -238,6 +239,7 @@ genvar w; shared_tlb_access_d = '1; shared_tlb_vaddr_d = itlb_vaddr_i; + i_req_d = 1; // we got an DTLB miss end else if ((|v_st_enbl_i[0][HYP_EXT:0]) & dtlb_access_i & ~dtlb_hit_i) begin @@ -252,6 +254,7 @@ genvar w; shared_tlb_access_d = '1; shared_tlb_vaddr_d = dtlb_vaddr_i; + i_req_d = 0; end end //itlb_dtlb_miss @@ -261,21 +264,21 @@ genvar w; itlb_update_o = '0; if(BYPASS==1) begin - if(shared_tlb_update_valid_delayed2) begin + if(shared_tlb_update_i.valid) begin shared_tlb_hit_d = 1'b1; if (itlb_req_q) begin itlb_update_o.valid = 1'b1; - itlb_update_o.vpn = shared_tlb_update_delayed2.vpn; - itlb_update_o.is_page = shared_tlb_update_delayed2.is_page; - itlb_update_o.content = shared_tlb_update_delayed2.content; - itlb_update_o.asid = shared_tlb_update_delayed2.asid; + itlb_update_o.vpn = shared_tlb_update_i.vpn; + itlb_update_o.is_page = shared_tlb_update_i.is_page; + itlb_update_o.content = shared_tlb_update_i.content; + itlb_update_o.asid = shared_tlb_update_i.asid; end else if (dtlb_req_q) begin dtlb_update_o.valid = 1'b1; - dtlb_update_o.vpn = shared_tlb_update_delayed2.vpn; - dtlb_update_o.is_page = shared_tlb_update_delayed2.is_page; - dtlb_update_o.content = shared_tlb_update_delayed2.content; - dtlb_update_o.asid = shared_tlb_update_delayed2.asid; + dtlb_update_o.vpn = shared_tlb_update_i.vpn; + dtlb_update_o.is_page = shared_tlb_update_i.is_page; + dtlb_update_o.content = shared_tlb_update_i.content; + dtlb_update_o.asid = shared_tlb_update_i.asid; end end end @@ -284,14 +287,14 @@ genvar w; for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin // first level match, this may be a giga 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) - match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req][0]) || !v_st_enbl_i[i_req][0]; + match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req_q][0]) || !v_st_enbl_i[i_req_q][0]; if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req][HYP_EXT]) || !v_st_enbl_i[i_req][HYP_EXT]; + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req_q][HYP_EXT]) || !v_st_enbl_i[i_req_q][HYP_EXT]; end // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req]; + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req_q]; if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin if (|level_match[i]) begin @@ -302,6 +305,7 @@ genvar w; 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]; + itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin itlb_update_o.asid[a] = tlb_update_asid_q[a]; end @@ -311,6 +315,7 @@ genvar w; 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]; + dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; for (int unsigned a = 0; a < HYP_EXT+1; a++) begin dtlb_update_o.asid[a] = tlb_update_asid_q[a]; end @@ -333,6 +338,7 @@ genvar w; vpn_q <= 0; itlb_req_q <= '0; dtlb_req_q <= '0; + i_req_q <= 0; shared_tag_valid <= '0; shared_tlb_update_valid_delayed<='0; shared_tlb_update_valid_delayed2<='0; @@ -348,6 +354,7 @@ genvar w; vpn_q <= vpn_d; itlb_req_q <= itlb_req_d; dtlb_req_q <= dtlb_req_d; + i_req_q <= i_req_d; shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; if(shared_tlb_update_i.valid) begin @@ -359,13 +366,6 @@ genvar w; shared_tlb_update_valid_delayed2 <= shared_tlb_update_valid_delayed; shared_tlb_update_delayed2 <= shared_tlb_update_delayed; - if(|flush_i) begin - shared_tlb_update_valid_delayed<='0; - shared_tlb_update_valid_delayed2<='0; - shared_tlb_update_delayed<='0; - shared_tlb_update_delayed2<='0; - end - end end @@ -385,7 +385,7 @@ genvar w; 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; - shared_tag_wr.v_st_enbl = v_st_enbl_i[i_req]; + end end end @@ -393,6 +393,7 @@ genvar w; assign shared_tag_wr.asid = shared_tlb_update_i.asid; assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; + assign shared_tag_wr.v_st_enbl = v_st_enbl_i[i_req_q]; genvar z; generate diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index def47577b9..ae0e088f70 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -71,8 +71,9 @@ logic [TLB_ENTRIES-1:0][HYP_EXT:0] match_asid; logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vpage_match; logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; -logic [TLB_ENTRIES-1:0] match_stage; +logic [TLB_ENTRIES-1:0] match_stage,tag_valid; pte_cva6_t g_content; +logic [TLB_ENTRIES-1:0] [(riscv::GPPNW-1):0] gppn; logic [HYP_EXT*2:0] v_st_enbl; assign v_st_enbl = (HYP_EXT==1) ? v_st_enbl_i : '1; @@ -110,14 +111,20 @@ for (i=0; i < TLB_ENTRIES; i++) begin assign vaddr_vpn_match[i][0][x] = vaddr_to_be_flushed_i[0][12+((VPN_LEN/PT_LEVELS)*(x+1))-1:12+((VPN_LEN/PT_LEVELS)*x)] == tags_q[i].vpn[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)) && update_i.valid && replace_en[i] && !lu_hit_o) ? update_i.vpn[(1+x)*(VPN_LEN/PT_LEVELS)-1:x*(VPN_LEN/PT_LEVELS)] : tags_q[i].vpn[x]; + end + assign tags_n[i].asid = ((~(|flush_i)) && update_i.valid && replace_en[i] && !lu_hit_o) ? update_i.asid : tags_q[i].asid; + assign tags_n[i].is_page = ((~(|flush_i)) && update_i.valid && replace_en[i] && !lu_hit_o) ? update_i.is_page : tags_q[i].is_page; + + assign tags_n[i].v_st_enbl = ((~(|flush_i)) && update_i.valid && replace_en[i] && !lu_hit_o) ? update_i.v_st_enbl: tags_q[i].v_st_enbl; + assign tags_n[i].valid = ((~(|flush_i)) && update_i.valid && replace_en[i] && !lu_hit_o) ? 1'b1 : tag_valid[i]; if(HYP_EXT==1) begin //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign tags_n[i].vpn[PT_LEVELS][(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][(VPN_LEN%PT_LEVELS)-1:0]; + assign tags_n[i].vpn[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] =((~(|flush_i)) && update_i.valid && replace_en[i] && !lu_hit_o) ? update_i.vpn[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : tags_q[i].vpn[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]; //identify if GPADDR matches the GPPN assign vaddr_vpn_match[i][HYP_EXT][0] = (vaddr_to_be_flushed_i[HYP_EXT][20:12] == gppn[i][8:0]); assign vaddr_vpn_match[i][HYP_EXT][HYP_EXT] = (vaddr_to_be_flushed_i[HYP_EXT][29:21] == gppn[i][17:9]); @@ -186,7 +193,7 @@ 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] [(riscv::GPPNW-1):0] gppn; + localparam int unsigned VADDR_WIDTH [1:0] = {riscv::GPLEN,riscv::VLEN}; genvar a; @@ -205,10 +212,11 @@ always_comb begin : update_flush 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; - tags_n[i].v_st_enbl =tags_q[i].v_st_enbl; + // 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; + tag_valid[i] = tags_q[i].valid; if(HYP_EXT==1) begin gppn[i] = make_gppn(tags_q[i].v_st_enbl[0], tags_q[i].is_page[0][0], tags_q[i].is_page[1][0], {tags_q[i].vpn[3][(VPN_LEN%PT_LEVELS)-1:0],tags_q[i].vpn[2],tags_q[i].vpn[1],tags_q[i].vpn[0]}, content_q[i][0]); @@ -220,56 +228,56 @@ always_comb begin : update_flush // invalidate logic // flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case) if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0] ) - tags_n[i].valid = 1'b0; + tag_valid[i] = 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[0] && (|vaddr_level_match[i][0] ) && (~vaddr_to_be_flushed_is0[0])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 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][0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] ) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 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[0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] ) && (!asid_to_be_flushed_is0[0])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; end end else if (flush_i[HYP_EXT] && HYP_EXT==1) begin if(tags_q[i].v_st_enbl[HYP_EXT*2] && tags_q[i].v_st_enbl[0]) begin // invalidate logic // flush everything if current VMID matches and ASID is 0 and vaddr is 0 ("SFENCE.VMA/HFENCE.VVMA x0 x0" case) if (asid_to_be_flushed_is0[0] && vaddr_to_be_flushed_is0[0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // flush vaddr in all addressing space if current VMID matches ("SFENCE.VMA/HFENCE.VVMA vaddr x0" case), it should happen only for leaf pages else if (asid_to_be_flushed_is0[0] && (|vaddr_level_match[i][0]) && (~vaddr_to_be_flushed_is0[0]) && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[1][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // the entry is flushed if it's not global and asid and vaddr and current VMID matches with the entry to be flushed ("SFENCE.VMA/HFENCE.VVMA vaddr asid" case) else if ((!content_q[i][0].g) && (|vaddr_level_match[i][0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!vaddr_to_be_flushed_is0[0]) && (!asid_to_be_flushed_is0[0])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // the entry is flushed if it's not global, and the asid and the current VMID matches and vaddr is 0. ("SFENCE.VMA/HFENCE.VVMA 0 asid" case) else if ((!content_q[i][0].g) && (vaddr_to_be_flushed_is0[0]) && (asid_to_be_flushed_i[0][ASID_WIDTH[0]-1:0] == tags_q[i].asid[0][ASID_WIDTH[0]-1:0] && ((tags_q[i].v_st_enbl[HYP_EXT] && lu_asid_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) || !tags_q[i].v_st_enbl[HYP_EXT])) && (!asid_to_be_flushed_is0[0])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; end end else if (flush_i[HYP_EXT*2] && HYP_EXT==1) begin if(tags_q[i].v_st_enbl[HYP_EXT]) begin // invalidate logic // flush everything if vmid is 0 and addr is 0 ("HFENCE.GVMA x0 x0" case) if (asid_to_be_flushed_is0[HYP_EXT] && vaddr_to_be_flushed_is0[HYP_EXT] ) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // flush gpaddr in all addressing space ("HFENCE.GVMA gpaddr x0" case), it should happen only for leaf pages else if (asid_to_be_flushed_is0[HYP_EXT] && (|vaddr_level_match[i][HYP_EXT] ) && (~vaddr_to_be_flushed_is0[HYP_EXT])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // the entry vmid and gpaddr both matches with the entry to be flushed ("HFENCE.GVMA gpaddr vmid" case) else if ((|vaddr_level_match[i][HYP_EXT]) && (asid_to_be_flushed_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) && (~vaddr_to_be_flushed_is0[HYP_EXT]) && (~asid_to_be_flushed_is0[HYP_EXT])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; // the entry is flushed if the vmid matches and gpaddr is 0. ("HFENCE.GVMA 0 vmid" case) else if ((vaddr_to_be_flushed_is0[HYP_EXT]) && (asid_to_be_flushed_i[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == tags_q[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0]) && (!asid_to_be_flushed_is0[HYP_EXT])) - tags_n[i].valid = 1'b0; + tag_valid[i] = 1'b0; end // normal replacement - end else if (update_i.valid & replace_en[i]) begin + end else if (update_i.valid & replace_en[i] && !lu_hit_o) begin // update tag array - tags_n[i].asid = update_i.asid; - tags_n[i].v_st_enbl= v_st_enbl; - 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].v_st_enbl= v_st_enbl; + // 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 159e53da2ed536822d6e7be5596b697d2a35e12f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 10:13:45 +0100 Subject: [PATCH 089/182] cleanup code, remove ASID_LEN parameter, remove BYPASS option in shared TLB --- core/load_store_unit.sv | 57 +----------------- core/mmu_unify/cva6_mmu.sv | 6 +- core/mmu_unify/cva6_shared_tlb.sv | 97 +++++++++++-------------------- core/mmu_unify/cva6_tlb.sv | 3 +- 4 files changed, 39 insertions(+), 124 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index a1ead8a5e8..a2a4a01e3e 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -140,59 +140,20 @@ 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 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 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), .HYP_EXT (HYP_EXT), .ASID_WIDTH ({ASID_WIDTH}), - .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( @@ -219,25 +180,11 @@ module load_store_unit .priv_lvl_i (priv_lvl_i ), .ld_st_priv_lvl_i (ld_st_priv_lvl_i ), - // connecting PTW to D$ IF - .sum_i ({sum_i}), .mxr_i ({mxr_i}), .hlvx_inst_i ( 0 ), - .hs_ld_st_inst_i ( 0 ), - - // icache address translation requests - - // .asid_to_be_flushed_i, - // .vmid_to_be_flushed_i, - // .vaddr_to_be_flushed_i, - // .gpaddr_to_be_flushed_i, - - - // Hypervisor load/store signals - - + .hs_ld_st_inst_i ( 0 ), .satp_ppn_i ({satp_ppn_i}), .asid_i ({asid_i}), diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 9aa149666d..af544fa4ad 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -25,7 +25,6 @@ module cva6_mmu import ariane_pkg::*; #( parameter int unsigned DATA_TLB_ENTRIES = 4, parameter logic HYP_EXT = 0, parameter int unsigned ASID_WIDTH [HYP_EXT:0], - parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 @@ -119,7 +118,7 @@ module cva6_mmu import ariane_pkg::*; #( logic valid; // valid flag 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; // + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled pte_cva6_t [HYP_EXT:0] content; } ; @@ -165,7 +164,6 @@ module cva6_mmu import ariane_pkg::*; #( .TLB_ENTRIES ( INSTR_TLB_ENTRIES), .HYP_EXT(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_itlb ( @@ -191,7 +189,6 @@ module cva6_mmu import ariane_pkg::*; #( .TLB_ENTRIES (DATA_TLB_ENTRIES), .HYP_EXT(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), .VPN_LEN(VPN_LEN), .PT_LEVELS(PT_LEVELS) ) i_dtlb ( @@ -218,7 +215,6 @@ module cva6_mmu import ariane_pkg::*; #( .HYP_EXT(HYP_EXT), .BYPASS(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), - .ASID_LEN (ASID_LEN), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS), .pte_cva6_t (pte_cva6_t), diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index bbb3b8600e..92f0407da2 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -29,9 +29,7 @@ module cva6_shared_tlb parameter int SHARED_TLB_DEPTH = 64, parameter int SHARED_TLB_WAYS = 2, parameter int unsigned HYP_EXT = 0, - parameter int unsigned BYPASS = 0, parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - parameter int unsigned ASID_LEN = 1, parameter int unsigned VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 ) ( @@ -84,7 +82,7 @@ function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(S endfunction typedef struct packed { -logic [HYP_EXT:0][ASID_LEN-1:0] asid; +logic [HYP_EXT:0][ASID_WIDTH[0]-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 @@ -160,8 +158,6 @@ assign shared_tlb_vaddr_o = shared_tlb_vaddr_q; assign itlb_req_o = itlb_req_q; -// assign i_req = itlb_req_q ? 1 : 0; - genvar i,x; generate for (i=0; i < SHARED_TLB_WAYS; i++) begin @@ -263,66 +259,43 @@ genvar w; dtlb_update_o = '0; itlb_update_o = '0; - if(BYPASS==1) begin - if(shared_tlb_update_i.valid) begin - shared_tlb_hit_d = 1'b1; - if (itlb_req_q) begin - itlb_update_o.valid = 1'b1; - itlb_update_o.vpn = shared_tlb_update_i.vpn; - itlb_update_o.is_page = shared_tlb_update_i.is_page; - itlb_update_o.content = shared_tlb_update_i.content; - itlb_update_o.asid = shared_tlb_update_i.asid; - - end else if (dtlb_req_q) begin - dtlb_update_o.valid = 1'b1; - dtlb_update_o.vpn = shared_tlb_update_i.vpn; - dtlb_update_o.is_page = shared_tlb_update_i.is_page; - dtlb_update_o.content = shared_tlb_update_i.content; - dtlb_update_o.asid = shared_tlb_update_i.asid; - end - end - end - else begin - //number of ways - for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin - // first level match, this may be a giga 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) - match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req_q][0]) || !v_st_enbl_i[i_req_q][0]; - - if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req_q][HYP_EXT]) || !v_st_enbl_i[i_req_q][HYP_EXT]; - end - - // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req_q]; - - if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin - if (|level_match[i]) 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]; - itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - itlb_update_o.asid[a] = tlb_update_asid_q[a]; - end - 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]; - dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - dtlb_update_o.asid[a] = tlb_update_asid_q[a]; - end + //number of ways + for (int unsigned i = 0; i < SHARED_TLB_WAYS; i++) begin + // first level match, this may be a giga 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) + match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req_q][0]) || !v_st_enbl_i[i_req_q][0]; + + if(HYP_EXT==1) begin + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req_q][HYP_EXT]) || !v_st_enbl_i[i_req_q][HYP_EXT]; + end + + // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req_q]; + + if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin + if (|level_match[i]) 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.content = pte[i]; + itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + itlb_update_o.asid[a] = tlb_update_asid_q[a]; + end + 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.content = pte[i]; + dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; + for (int unsigned a = 0; a < HYP_EXT+1; a++) begin + dtlb_update_o.asid[a] = tlb_update_asid_q[a]; end end end - end + end end end //tag_comparison diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index ae0e088f70..e94a221ef4 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -28,7 +28,6 @@ module cva6_tlb import ariane_pkg::*; #( parameter int unsigned TLB_ENTRIES = 4, parameter int unsigned HYP_EXT = 0, 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 VPN_LEN = 1, parameter int unsigned PT_LEVELS = 1 )( @@ -52,7 +51,7 @@ output logic lu_hit_o // SV39 defines three levels of page tables struct packed { -logic [HYP_EXT:0][ASID_LEN-1:0] asid; +logic [HYP_EXT:0][ASID_WIDTH[0]-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 From 27a0e39467d89a4cad34cc3cef48bed28bb2d099 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 10:17:51 +0100 Subject: [PATCH 090/182] remove bypass option at top mmu --- core/mmu_unify/cva6_mmu.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index af544fa4ad..4e62dcd1cc 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -213,7 +213,6 @@ module cva6_mmu import ariane_pkg::*; #( .SHARED_TLB_DEPTH (64), .SHARED_TLB_WAYS (2), .HYP_EXT(HYP_EXT), - .BYPASS(HYP_EXT), .ASID_WIDTH (ASID_WIDTH), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS), From 300bee5abacdac3a3f9856640d04910c49590477 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 10:29:01 +0100 Subject: [PATCH 091/182] fix assignment of asid_width in LSU for simulation --- core/load_store_unit.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index a2a4a01e3e..5487ec7f0b 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -146,6 +146,7 @@ module load_store_unit localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn 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}; cva6_mmu #( @@ -153,7 +154,7 @@ module load_store_unit .INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES), .DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES), .HYP_EXT (HYP_EXT), - .ASID_WIDTH ({ASID_WIDTH}), + .ASID_WIDTH (mmu_ASID_WIDTH), .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( From ba208be72b5cfdcea77e622b087c8c11127ec389 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 10:59:42 +0100 Subject: [PATCH 092/182] clean up package files --- core/Flist.cva6 | 11 --------- core/include/ariane_pkg.sv | 30 ------------------------- core/include/riscv_pkg.sv | 7 +++--- core/load_store_unit.sv | 16 ++++++------- core/mmu_unify/cva6_tlb.sv | 46 +++++++++++++++++++++++++++++--------- 5 files changed, 46 insertions(+), 64 deletions(-) diff --git a/core/Flist.cva6 b/core/Flist.cva6 index aa8f20b281..636398304d 100644 --- a/core/Flist.cva6 +++ b/core/Flist.cva6 @@ -176,17 +176,6 @@ ${CVA6_REPO_DIR}/common/local/util/tc_sram_wrapper.sv ${CVA6_REPO_DIR}/vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv ${CVA6_REPO_DIR}/common/local/util/sram.sv -// MMU Sv39 -${CVA6_REPO_DIR}/core/mmu_sv39/mmu.sv -${CVA6_REPO_DIR}/core/mmu_sv39/ptw.sv -${CVA6_REPO_DIR}/core/mmu_sv39/tlb.sv - -// MMU Sv32 -${CVA6_REPO_DIR}/core/mmu_sv32/cva6_mmu_sv32.sv -${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 diff --git a/core/include/ariane_pkg.sv b/core/include/ariane_pkg.sv index 81234d311e..446f609a61 100644 --- a/core/include/ariane_pkg.sv +++ b/core/include/ariane_pkg.sv @@ -1004,34 +1004,4 @@ package ariane_pkg; default: return 2'b11; endcase endfunction - - // computes the paddr based on the page size, ppn and offset - function automatic logic [(riscv::GPLEN-1):0] make_gpaddr(input logic s_st_enbl, input logic is_1G, input logic is_2M, input logic [(riscv::VLEN-1):0] vaddr, input riscv::pte_t pte); - logic [(riscv::GPLEN-1):0] gpaddr; - if (s_st_enbl) begin - gpaddr = {pte.ppn[(riscv::GPPNW-1):0], vaddr[11:0]}; - // Giga page - if (is_1G) gpaddr[29:12] = vaddr[29:12]; - // Mega page - if (is_2M) gpaddr[20:12] = vaddr[20:12]; - end else begin - gpaddr = vaddr[(riscv::GPLEN-1):0]; - end - return gpaddr; -endfunction : make_gpaddr - -// computes the final gppn based on the guest physical address -function automatic logic [(riscv::GPPNW-1):0] make_gppn(input logic s_st_enbl, input logic is_1G, input logic is_2M, input logic [28:0] vpn, input riscv::pte_t pte); - logic [(riscv::GPPNW-1):0] gppn; - if (s_st_enbl) begin - gppn = pte.ppn[(riscv::GPPNW-1):0]; - if(is_2M) - gppn[8:0] = vpn[8:0]; - if(is_1G) - gppn[17:0] = vpn[17:0]; - end else begin - gppn = vpn; - end - return gppn; -endfunction : make_gppn endpackage diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index 729a7e6748..e6d6a9484f 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -39,9 +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 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; @@ -323,7 +323,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 5487ec7f0b..a1fc67f4a5 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -143,9 +143,9 @@ module load_store_unit if (MMU_PRESENT) begin : gen_mmu - localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn - localparam VPN_LEN = (riscv::XLEN == 64) ? (HYP_EXT ? 29 : 27) : 20; - localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn + 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}; @@ -158,13 +158,13 @@ module load_store_unit .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .flush_i(flush_i), + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (flush_i), .enable_translation_i ({enable_translation_i}), .en_ld_st_translation_i ({en_ld_st_translation_i}), - .icache_areq_i ( icache_areq_i ), - .icache_areq_o ( icache_areq_o ), + .icache_areq_i ( icache_areq_i), + .icache_areq_o ( icache_areq_o), // misaligned bypass .misaligned_ex_i ( misaligned_exception ), .lsu_req_i ( translation_req ), diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e94a221ef4..e8642e58b4 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -49,6 +49,40 @@ output logic [PT_LEVELS-2:0] lu_is_page_o, output logic lu_hit_o ); + // computes the paddr based on the page size, ppn and offset +function automatic logic [(riscv::GPLEN-1):0] make_gpaddr( + input logic s_st_enbl, input logic is_1G, input logic is_2M, + input logic [(riscv::VLEN-1):0] vaddr, input riscv::pte_t pte); + logic [(riscv::GPLEN-1):0] gpaddr; + if (s_st_enbl) begin + gpaddr = {pte.ppn[(riscv::GPPNW-1):0], vaddr[11:0]}; + // Giga page + if (is_1G) gpaddr[29:12] = vaddr[29:12]; + // Mega page + if (is_2M) gpaddr[20:12] = vaddr[20:12]; + end else begin + gpaddr = vaddr[(riscv::GPLEN-1):0]; + end +return gpaddr; +endfunction : make_gpaddr + +// computes the final gppn based on the guest physical address +function automatic logic [(riscv::GPPNW-1):0] make_gppn( + input logic s_st_enbl, input logic is_1G, input logic is_2M, + input logic [28:0] vpn, input riscv::pte_t pte); + logic [(riscv::GPPNW-1):0] gppn; + if (s_st_enbl) begin + gppn = pte.ppn[(riscv::GPPNW-1):0]; + if(is_2M) + gppn[8:0] = vpn[8:0]; + if(is_1G) + gppn[17:0] = vpn[17:0]; + end else begin + gppn = vpn; + end +return gppn; +endfunction : make_gppn + // SV39 defines three levels of page tables struct packed { logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; @@ -211,10 +245,6 @@ always_comb begin : update_flush 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; - // tags_n[i].v_st_enbl =tags_q[i].v_st_enbl; tag_valid[i] = tags_q[i].valid; if(HYP_EXT==1) begin @@ -272,13 +302,7 @@ always_comb begin : update_flush end // normal replacement end else if (update_i.valid & replace_en[i] && !lu_hit_o) begin - // update tag array - // tags_n[i].asid = update_i.asid; - // tags_n[i].v_st_enbl= v_st_enbl; - // tags_n[i].is_page= update_i.is_page; - // tags_n[i].valid= 1'b1; - - // and content as well + // update content as well content_n[i] = update_i.content; end end From dd431e3f5c04752b57699fbc91c1b5f5f960919f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:06:25 +0100 Subject: [PATCH 093/182] linting --- core/load_store_unit.sv | 18 +- core/mmu_unify/cva6_mmu.sv | 448 ++++++++++++++++++------------------- 2 files changed, 233 insertions(+), 233 deletions(-) diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index a1fc67f4a5..7db44be9a1 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -145,8 +145,8 @@ module load_store_unit localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn 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}; + localparam PT_LEVELS = (riscv::XLEN == 64) ? 3 : 2; + localparam int unsigned mmu_ASID_WIDTH[HYP_EXT:0] = {ASID_WIDTH}; cva6_mmu #( @@ -158,13 +158,13 @@ module load_store_unit .VPN_LEN (VPN_LEN), .PT_LEVELS (PT_LEVELS) ) i_cva6_mmu ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i (flush_i), - .enable_translation_i ({enable_translation_i}), - .en_ld_st_translation_i ({en_ld_st_translation_i}), - .icache_areq_i ( icache_areq_i), - .icache_areq_o ( icache_areq_o), + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (flush_i), + .enable_translation_i ({enable_translation_i}), + .en_ld_st_translation_i({en_ld_st_translation_i}), + .icache_areq_i (icache_areq_i), + .icache_areq_o (icache_areq_o), // misaligned bypass .misaligned_ex_i ( misaligned_exception ), .lsu_req_i ( translation_req ), diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 4e62dcd1cc..ff6964a3e3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -83,236 +83,236 @@ module cva6_mmu import ariane_pkg::*; #( input riscv::pmpcfg_t [15:0] pmpcfg_i, input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); - logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i [HYP_EXT:0]; - logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i [HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i [HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i [HYP_EXT:0]; + +genvar b; +generate + for (b=0; b < HYP_EXT+1; b++) begin + assign dtlb_mmu_asid_i[b] = b==0 ? + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + assign itlb_mmu_asid_i[b] = b==0 ? + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + end +endgenerate + +// 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][HYP_EXT:0] is_page; // + logic [VPN_LEN-1:0] vpn; // + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; +} ; + +logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page +logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + +logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +pte_cva6_t [HYP_EXT:0] itlb_content ; +logic [PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; +logic [riscv::GPLEN-1:0] itlb_gpaddr; +logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + +logic dtlb_lu_access; +pte_cva6_t [HYP_EXT:0] dtlb_content ; +logic [PT_LEVELS-2:0] dtlb_is_page; +logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; +logic dtlb_lu_hit; +logic [riscv::GPLEN-1:0] dtlb_gpaddr; + +logic shared_tlb_access; +logic shared_tlb_hit, itlb_req; + +// Assignments + +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( enable_translation_i ), + .update_i ( update_itlb ), + .lu_access_i ( itlb_lu_access ), + .lu_asid_i ( itlb_mmu_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_gpaddr_o ( itlb_gpaddr ), + .lu_is_page_o ( itlb_is_page ), + .lu_hit_o ( itlb_lu_hit ) +); - genvar b; - generate - for (b=0; b < HYP_EXT+1; b++) begin - assign dtlb_mmu_asid_i[b] = b==0 ? - ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? - (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - end - endgenerate +cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( en_ld_st_translation_i ), + .update_i ( update_dtlb ), + .lu_access_i ( dtlb_lu_access ), + .lu_asid_i ( dtlb_mmu_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_gpaddr_o ( dtlb_gpaddr ), + .lu_is_page_o ( dtlb_is_page ), + .lu_hit_o ( dtlb_lu_hit ) +); + + +cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT(HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), +// .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT(HYP_EXT), + .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 ), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_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 ), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o ( update_vaddr ), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), - // 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][HYP_EXT:0] is_page; // - logic [VPN_LEN-1:0] vpn; // - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; - } ; - - logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page - logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - - logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - pte_cva6_t [HYP_EXT:0] itlb_content ; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - logic [riscv::GPLEN-1:0] itlb_gpaddr; - logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - - logic dtlb_lu_access; - pte_cva6_t [HYP_EXT:0] dtlb_content ; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; - logic dtlb_lu_hit; - logic [riscv::GPLEN-1:0] dtlb_gpaddr; - - logic shared_tlb_access; - logic shared_tlb_hit, itlb_req; - - // Assignments - - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( enable_translation_i ), - .update_i ( update_itlb ), - .lu_access_i ( itlb_lu_access ), - .lu_asid_i ( itlb_mmu_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_gpaddr_o ( itlb_gpaddr ), - .lu_is_page_o ( itlb_is_page ), - .lu_hit_o ( itlb_lu_hit ) - ); +// 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i ( hlvx_inst_i ), + // from CSR file + .satp_ppn_i (satp_ppn_i ), + .mxr_i (mxr_i ), - cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( en_ld_st_translation_i ), - .update_i ( update_dtlb ), - .lu_access_i ( dtlb_lu_access ), - .lu_asid_i ( dtlb_mmu_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_gpaddr_o ( dtlb_gpaddr ), - .lu_is_page_o ( dtlb_is_page ), - .lu_hit_o ( dtlb_lu_hit ) - ); + // Performance counters + .shared_tlb_miss_o(), //open for now +// PMP + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) - cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT(HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT(HYP_EXT), - .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 ), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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 ), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o ( update_vaddr ), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i ( hlvx_inst_i ), - // from CSR file - .satp_ppn_i (satp_ppn_i ), - .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 From ff708b32e530117ed1bee95bbfdb2cfe8b75706b Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:13:49 +0100 Subject: [PATCH 094/182] linting --- core/mmu_unify/cva6_mmu.sv | 428 ++++++++++++++++++------------------- 1 file changed, 214 insertions(+), 214 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index ff6964a3e3..d51e46b95c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -83,12 +83,12 @@ module cva6_mmu import ariane_pkg::*; #( input riscv::pmpcfg_t [15:0] pmpcfg_i, input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i [HYP_EXT:0]; -logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i [HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; -genvar b; -generate - for (b=0; b < HYP_EXT+1; b++) begin + genvar b; + generate + for (b = 0; b < HYP_EXT + 1; b++) begin assign dtlb_mmu_asid_i[b] = b==0 ? ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; @@ -96,223 +96,223 @@ generate (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; end -endgenerate - -// 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][HYP_EXT:0] is_page; // - logic [VPN_LEN-1:0] vpn; // - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; -} ; - -logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page -logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - -logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - -logic itlb_lu_access; -pte_cva6_t [HYP_EXT:0] itlb_content ; -logic [PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; -logic [riscv::GPLEN-1:0] itlb_gpaddr; -logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - -logic dtlb_lu_access; -pte_cva6_t [HYP_EXT:0] dtlb_content ; -logic [PT_LEVELS-2:0] dtlb_is_page; -logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; -logic dtlb_lu_hit; -logic [riscv::GPLEN-1:0] dtlb_gpaddr; - -logic shared_tlb_access; -logic shared_tlb_hit, itlb_req; - -// Assignments - -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( enable_translation_i ), - .update_i ( update_itlb ), - .lu_access_i ( itlb_lu_access ), - .lu_asid_i ( itlb_mmu_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_gpaddr_o ( itlb_gpaddr ), - .lu_is_page_o ( itlb_is_page ), - .lu_hit_o ( itlb_lu_hit ) -); - -cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( en_ld_st_translation_i ), - .update_i ( update_dtlb ), - .lu_access_i ( dtlb_lu_access ), - .lu_asid_i ( dtlb_mmu_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_gpaddr_o ( dtlb_gpaddr ), - .lu_is_page_o ( dtlb_is_page ), - .lu_hit_o ( dtlb_lu_hit ) -); - - -cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT(HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), + endgenerate - .itlb_req_o (itlb_req), - // to update shared tlb - .shared_tlb_update_i(update_shared_tlb) -); + // 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][HYP_EXT:0] is_page; // + logic [VPN_LEN-1:0] vpn; // + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; + } ; + + logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + pte_cva6_t [HYP_EXT:0] itlb_content ; + logic [PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + logic [riscv::GPLEN-1:0] itlb_gpaddr; + logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + + logic dtlb_lu_access; + pte_cva6_t [HYP_EXT:0] dtlb_content ; + logic [PT_LEVELS-2:0] dtlb_is_page; + logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; + logic dtlb_lu_hit; + logic [riscv::GPLEN-1:0] dtlb_gpaddr; + + logic shared_tlb_access; + logic shared_tlb_hit, itlb_req; + + // Assignments + + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES ( INSTR_TLB_ENTRIES), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( enable_translation_i ), + .update_i ( update_itlb ), + .lu_access_i ( itlb_lu_access ), + .lu_asid_i ( itlb_mmu_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_gpaddr_o ( itlb_gpaddr ), + .lu_is_page_o ( itlb_is_page ), + .lu_hit_o ( itlb_lu_hit ) + ); -cva6_ptw #( - .CVA6Cfg (CVA6Cfg), -// .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT(HYP_EXT), - .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 ), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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 ), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), + cva6_tlb #( + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT(HYP_EXT), + .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 ), + .v_st_enbl_i ( en_ld_st_translation_i ), + .update_i ( update_dtlb ), + .lu_access_i ( dtlb_lu_access ), + .lu_asid_i ( dtlb_mmu_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_gpaddr_o ( dtlb_gpaddr ), + .lu_is_page_o ( dtlb_is_page ), + .lu_hit_o ( dtlb_lu_hit ) + ); - .update_vaddr_o ( update_vaddr ), - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), + cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT(HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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) + ); - -// 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i ( hlvx_inst_i ), - // from CSR file - .satp_ppn_i (satp_ppn_i ), - .mxr_i (mxr_i ), + cva6_ptw #( + .CVA6Cfg (CVA6Cfg), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t(pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT(HYP_EXT), + .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 ), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_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 ), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o ( update_vaddr ), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), - // Performance counters - .shared_tlb_miss_o(), //open for now + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i ( hlvx_inst_i ), + // from CSR file + .satp_ppn_i (satp_ppn_i ), + .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) + // 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 From 8865ce5ecda5441cc41144e5682edc3334b1d4e9 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:18:54 +0100 Subject: [PATCH 095/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/include/riscv_pkg.sv | 14 ++++----- core/load_store_unit.sv | 65 +++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/core/include/riscv_pkg.sv b/core/include/riscv_pkg.sv index e6d6a9484f..da1ebe00eb 100644 --- a/core/include/riscv_pkg.sv +++ b/core/include/riscv_pkg.sv @@ -39,9 +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 GPLEN = (XLEN == 32) ? 34 : 41; localparam GPPNW = (XLEN == 32) ? 22 : 29; - localparam GPPN2 = (XLEN == 32) ? riscv::VLEN-33 : 10; + 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; @@ -368,12 +368,12 @@ package riscv; 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); - // ---------------------- - // PseudoInstructions Codes - // ---------------------- - localparam logic [XLEN-1:0] READ_32_PSEUDOINSTRUCTION = 32'h00002000; + // ---------------------- + // PseudoInstructions Codes + // ---------------------- + localparam logic [XLEN-1:0] READ_32_PSEUDOINSTRUCTION = 32'h00002000; localparam logic [XLEN-1:0] WRITE_32_PSEUDOINSTRUCTION = 32'h00002020; - localparam logic [XLEN-1:0] READ_64_PSEUDOINSTRUCTION = 64'h00003000; + localparam logic [XLEN-1:0] READ_64_PSEUDOINSTRUCTION = 64'h00003000; localparam logic [XLEN-1:0] WRITE_64_PSEUDOINSTRUCTION = 64'h00003020; // ----- diff --git a/core/load_store_unit.sv b/core/load_store_unit.sv index 7db44be9a1..5abed4452e 100644 --- a/core/load_store_unit.sv +++ b/core/load_store_unit.sv @@ -140,7 +140,6 @@ module load_store_unit // ------------------- // MMU e.g.: TLBs/PTW // ------------------- - if (MMU_PRESENT) begin : gen_mmu localparam HYP_EXT = 0; //CVA6Cfg.CVA6ConfigHExtEn @@ -166,38 +165,38 @@ module load_store_unit .icache_areq_i (icache_areq_i), .icache_areq_o (icache_areq_o), // misaligned bypass - .misaligned_ex_i ( misaligned_exception ), - .lsu_req_i ( translation_req ), - .lsu_vaddr_i ( mmu_vaddr ), - .lsu_tinst_i ( 0 ), - .lsu_is_store_i ( st_translation_req ), - .csr_hs_ld_st_inst_o ( ), - .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 - - .lsu_valid_o ( translation_valid ), - .lsu_paddr_o ( mmu_paddr ), - .lsu_exception_o ( mmu_exception ), - - .priv_lvl_i (priv_lvl_i ), - .ld_st_priv_lvl_i (ld_st_priv_lvl_i ), - - .sum_i ({sum_i}), - .mxr_i ({mxr_i}), - .hlvx_inst_i ( 0 ), - .hs_ld_st_inst_i ( 0 ), - - .satp_ppn_i ({satp_ppn_i}), - .asid_i ({asid_i}), - .asid_to_be_flushed_i ({asid_to_be_flushed_i}), - .vaddr_to_be_flushed_i ({vaddr_to_be_flushed_i}), - .flush_tlb_i ({flush_tlb_i}), - - .itlb_miss_o (itlb_miss_o), - .dtlb_miss_o (dtlb_miss_o), - - .req_port_i ( dcache_req_ports_i [0] ), - .req_port_o ( dcache_req_ports_o [0] ), + .misaligned_ex_i (misaligned_exception), + .lsu_req_i (translation_req), + .lsu_vaddr_i (mmu_vaddr), + .lsu_tinst_i (0), + .lsu_is_store_i (st_translation_req), + .csr_hs_ld_st_inst_o (), + .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 + + .lsu_valid_o (translation_valid), + .lsu_paddr_o (mmu_paddr), + .lsu_exception_o(mmu_exception), + + .priv_lvl_i (priv_lvl_i), + .ld_st_priv_lvl_i(ld_st_priv_lvl_i), + + .sum_i ({sum_i}), + .mxr_i ({mxr_i}), + .hlvx_inst_i (0), + .hs_ld_st_inst_i(0), + + .satp_ppn_i ({satp_ppn_i}), + .asid_i ({asid_i}), + .asid_to_be_flushed_i ({asid_to_be_flushed_i}), + .vaddr_to_be_flushed_i({vaddr_to_be_flushed_i}), + .flush_tlb_i ({flush_tlb_i}), + + .itlb_miss_o(itlb_miss_o), + .dtlb_miss_o(dtlb_miss_o), + + .req_port_i(dcache_req_ports_i[0]), + .req_port_o(dcache_req_ports_o[0]), .pmpcfg_i, .pmpaddr_i ); From a06d54faa11a9b8b4c071ef331007ce92ffa1612 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:20:08 +0100 Subject: [PATCH 096/182] Update core/mmu_unify/cva6_mmu.sv Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d51e46b95c..292fa2da3c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -18,8 +18,10 @@ // by Florian Zaruba, the MMU Sv32 developed by Sebastien Jacq and the MMU Sv39x4 developed by Bruno Sá. -module cva6_mmu import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, +module cva6_mmu + import ariane_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now parameter int unsigned INSTR_TLB_ENTRIES = 4, parameter int unsigned DATA_TLB_ENTRIES = 4, From c57fdc61ddee352c4ab38d9099dcc8517cf60c05 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:22:48 +0100 Subject: [PATCH 097/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 98 +++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 292fa2da3c..656279c795 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -23,85 +23,85 @@ module cva6_mmu #( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 - + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] input logic [HYP_EXT*2:0] 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, + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction + 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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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) + 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 + 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 [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, + input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, + input logic [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] - 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[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] flush_tlb_i, + input logic [HYP_EXT*2:0] flush_tlb_i, // Performance counters - output logic itlb_miss_o, - output logic dtlb_miss_o, + 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, + 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 riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i ); -logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; -logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; - genvar b; - generate - for (b = 0; b < HYP_EXT + 1; b++) begin - assign dtlb_mmu_asid_i[b] = b==0 ? + genvar b; + generate + for (b = 0; b < HYP_EXT + 1; b++) begin + assign dtlb_mmu_asid_i[b] = b==0 ? ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? + assign itlb_mmu_asid_i[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; end endgenerate - // memory management, pte for cva6 - localparam type pte_cva6_t = struct packed { + // 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; From f33cf80e86cfcf58e50cf06fdd79e48a0208e570 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:27:22 +0100 Subject: [PATCH 098/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 100 ++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 656279c795..fa398c6cf3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -347,63 +347,59 @@ module cva6_mmu (|enable_translation_i[HYP_EXT:0]) ? // (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - 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[HYP_EXT:0] && (|itlb_is_page[a:0]==0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 + (riscv::PLEN-PPNWMin-1)'(icache_areq_i.fetch_vaddr[((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + genvar a; + generate - endgenerate + 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[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 - // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if(HYP_EXT==1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - end icache_areq_o.fetch_valid = 1'b0; From 0ddafaffc088a028b2fc253de54a35a7b41216b8 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:30:12 +0100 Subject: [PATCH 099/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index fa398c6cf3..c8cd34d763 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -98,7 +98,7 @@ module cva6_mmu (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; end - endgenerate + endgenerate // memory management, pte for cva6 localparam type pte_cva6_t = struct packed { From 78ad38f3ff11aca03dfc212570fe4b21bd0c4188 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:32:34 +0100 Subject: [PATCH 100/182] linting --- core/mmu_unify/cva6_mmu.sv | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c8cd34d763..ed132e7ff0 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -102,18 +102,18 @@ module cva6_mmu // 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; - } ; + // 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 { From a8c527c1135fee9442cfcfa0c02b4a24b8055009 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:35:05 +0100 Subject: [PATCH 101/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index ed132e7ff0..57ae797a3e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -291,8 +291,8 @@ module cva6_mmu .shared_tlb_update_o(update_shared_tlb), - // from shared TLB - // did we miss? + // 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), @@ -309,7 +309,7 @@ module cva6_mmu // Performance counters .shared_tlb_miss_o(), //open for now - // PMP + // PMP .pmpcfg_i (pmpcfg_i), .pmpaddr_i (pmpaddr_i), .bad_paddr_o(ptw_bad_paddr) From a5dea8ad00a3a158ffc6151a4278adf6fc6357a9 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:38:11 +0100 Subject: [PATCH 102/182] linting --- core/mmu_unify/cva6_mmu.sv | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 57ae797a3e..458de05afe 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -382,23 +382,23 @@ module cva6_mmu // an error. if ((|enable_translation_i[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else 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; From 9b2d73a6cd51e77ae8a791742af4452b591689f6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:39:47 +0100 Subject: [PATCH 103/182] linting --- core/mmu_unify/cva6_mmu.sv | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 458de05afe..ed92b6a8a5 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -400,10 +400,8 @@ module cva6_mmu }; end - icache_areq_o.fetch_valid = 1'b0; - // --------- // ITLB Hit // -------- From ff8807843db36c023b1df4f7ce618ad180b50195 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:42:16 +0100 Subject: [PATCH 104/182] linting --- 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 ed92b6a8a5..1452b70142 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -510,6 +510,7 @@ module cva6_mmu end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From f24e7b7f6a98394bb29220726df9f29cd192aa9f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 11:48:57 +0100 Subject: [PATCH 105/182] linting --- core/mmu_unify/cva6_mmu.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1452b70142..ed92b6a8a5 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -510,7 +510,6 @@ module cva6_mmu end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From 1a4218b86763dcd80a5d9b12f545a1ac3ac78678 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:52:25 +0100 Subject: [PATCH 106/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 83 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index ed92b6a8a5..d2ae6f6c78 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -576,60 +576,59 @@ module cva6_mmu assign lsu_paddr_o [11:0] = lsu_vaddr_q[0][11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - assign lsu_dtlb_ppn_o [11:0] = - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][23:12]; + lsu_vaddr_n[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + 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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: dtlb_pte_q[0].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[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]; + lsu_vaddr_q[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; - end + 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[0] = lsu_vaddr_i; - lsu_tinst_n = lsu_tinst_i; - - lsu_req_n = lsu_req_i; - hs_ld_st_inst_n = hs_ld_st_inst_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; - - if(HYP_EXT==1) begin - lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; - 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[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_i; + + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_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; + + if (HYP_EXT == 1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + end lsu_valid_o = lsu_req_q; lsu_exception_o = misaligned_ex_q; From fc7e10a9631a7930ca148e64cd2fb73d757f9952 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:54:24 +0100 Subject: [PATCH 107/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d2ae6f6c78..be063acaef 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -630,17 +630,17 @@ module cva6_mmu lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; end - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_q; - pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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; + // 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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); From e46d3989e5b242367f8f9ea650697aa14008e34b Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:05:08 +0100 Subject: [PATCH 108/182] linting --- core/mmu_unify/cva6_mmu.sv | 205 +++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 102 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index be063acaef..04664709ed 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -399,137 +399,138 @@ module cva6_mmu 1'b1 }; end + end - icache_areq_o.fetch_valid = 1'b0; + 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 }; - end else begin - if (HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - end - end end - // TODO(moschn,zarubaf): What should the value of tval be in this case? + + end else if (!pmp_instr_allow) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin - if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end else begin + if (HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 }; end end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end + // TODO(moschn,zarubaf): What should the value of tval be in this case? else 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 - }; + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 + 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}); From debfbf7bd15e80dec17fcda6c488f602ea4a59c5 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:09:34 +0100 Subject: [PATCH 109/182] linting --- core/mmu_unify/cva6_mmu.sv | 278 ++++++++++++++++++------------------- 1 file changed, 139 insertions(+), 139 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 04664709ed..8871838338 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -361,176 +361,176 @@ module cva6_mmu 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_GUEST_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, + itlb_gpaddr[riscv::GPLEN-1:0], {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 }; - end + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; + end else if (!pmp_instr_allow) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end else begin + if (HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end + end end - 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end else begin - if (HYP_EXT==1) begin + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], + enable_translation_i[HYP_EXT*2], 1'b1 }; end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; end end end - // TODO(moschn,zarubaf): What should the value of tval be in this case? + 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; - end + 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 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else 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 - 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}); From 9f5ff3cac216f337f12bd3ad65d60e23d2ffd8a3 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:11:22 +0100 Subject: [PATCH 110/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 8871838338..e5cbe973de 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -113,9 +113,9 @@ module cva6_mmu logic w; logic r; logic v; -} ; + }; - localparam type tlb_update_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; // From cc9bd21659154eb1504dc033dee344c6911934ec Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:19:36 +0100 Subject: [PATCH 111/182] linting --- core/mmu_unify/cva6_mmu.sv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e5cbe973de..d7b94d6aed 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -117,12 +117,12 @@ module cva6_mmu 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 [VPN_LEN-1:0] vpn; // - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; + logic valid; // valid flag + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; // + logic [VPN_LEN-1:0] vpn; // + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; } ; logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page From 8f574c7f642dc4187b0aea224958f7a46474f942 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:20:38 +0100 Subject: [PATCH 112/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 74 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d7b94d6aed..f712df6234 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -361,45 +361,45 @@ module cva6_mmu 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - end + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end + end icache_areq_o.fetch_valid = 1'b0; From a429c1daf6f13837079e9b3e7c650bf671123f54 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:21:35 +0100 Subject: [PATCH 113/182] linting --- core/mmu_unify/cva6_mmu.sv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f712df6234..c8d693a3d0 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -117,10 +117,10 @@ module cva6_mmu 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 [VPN_LEN-1:0] vpn; // - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; // + logic valid; // valid flag + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled pte_cva6_t [HYP_EXT:0] content; } ; From 41801297ebb528c49803c1fd01f57c8a676ec9e0 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:27:59 +0100 Subject: [PATCH 114/182] linting --- core/mmu_unify/cva6_mmu.sv | 165 +++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c8d693a3d0..97426bb1ec 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -381,82 +381,82 @@ module cva6_mmu // AXI decode error), or when PTW performs walk due to ITLB miss and raises // an error. if ((|enable_translation_i[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + // 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end end - end - icache_areq_o.fetch_valid = 1'b0; + 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + end - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end + end else if (!pmp_instr_allow) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; end - end else + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end + end else begin // --------- // ITLB Miss // --------- @@ -511,26 +511,27 @@ module cva6_mmu end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 + 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}); From 911da2d1438b3be6ebc60e283ad06638e99d6a59 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:30:24 +0100 Subject: [PATCH 115/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 97426bb1ec..af1fc9562e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -118,9 +118,9 @@ module cva6_mmu 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 [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled pte_cva6_t [HYP_EXT:0] content; } ; From 8623338e2118bb19c405ad384d28409f90db472f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:32:03 +0100 Subject: [PATCH 116/182] linting --- core/mmu_unify/cva6_mmu.sv | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index af1fc9562e..c222bada6a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -384,20 +384,20 @@ module cva6_mmu // 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 if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end end From 1206093fd7867330b28d98fe2eccfbb4fc90a44e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:33:12 +0100 Subject: [PATCH 117/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c222bada6a..f61255842e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -118,9 +118,9 @@ module cva6_mmu 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 [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled pte_cva6_t [HYP_EXT:0] content; } ; From d69a046b6682d7a23599e5a3540f8a9848d75341 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:43:34 +0100 Subject: [PATCH 118/182] linting --- core/mmu_unify/cva6_mmu.sv | 290 ++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 145 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f61255842e..e55aa596ca 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -348,155 +348,102 @@ module cva6_mmu (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - 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[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 - - 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - 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[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 - 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin + 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin + end else 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 - }; + }; end + end - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end - end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; - end else begin - if (HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 }; - end end - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else begin + + end else if (!pmp_instr_allow) begin if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -505,33 +452,86 @@ module cva6_mmu end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end + end else begin + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end else begin + if (HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end + end + end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end end end end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else 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 - }; + // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 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}); From fb4e6bc034e5a30ddc77de71c415d422815a41fb Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:47:43 +0100 Subject: [PATCH 119/182] linting --- core/mmu_unify/cva6_mmu.sv | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e55aa596ca..62e023cdc1 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -577,39 +577,39 @@ module cva6_mmu assign lsu_paddr_o [11:0] = lsu_vaddr_q[0][11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: - dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][23:12]; + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: - dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? - lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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)])); + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): - lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; end endgenerate From 75593ef48a82ddd3ed41f35d6b0733b2c02824df Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:49:25 +0100 Subject: [PATCH 120/182] linting --- core/mmu_unify/cva6_mmu.sv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 62e023cdc1..08bd83d36a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -117,12 +117,12 @@ module cva6_mmu 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 [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; + logic valid; // valid flag + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; } ; logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page From 714ae600b1ac92e5afe7857224cfca6a84d5563e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:50:38 +0100 Subject: [PATCH 121/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 08bd83d36a..62a592eafb 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -348,16 +348,16 @@ module cva6_mmu (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 + 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[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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)))]: + 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[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 + icache_areq_i.fetch_vaddr[PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1]; + end endgenerate From 47339bef0695c35cc7346bf75507408321e7b9bb Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:52:05 +0100 Subject: [PATCH 122/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 62a592eafb..13c27d862c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -117,7 +117,7 @@ module cva6_mmu localparam type tlb_update_cva6_t = struct packed { // typedef struct packed { - logic valid; // valid flag + logic valid; logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; logic [VPN_LEN-1:0] vpn; logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; @@ -359,7 +359,7 @@ module cva6_mmu 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 From 4c6254bd695fafcec1e2b3b1419d43bb051b0d31 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:53:02 +0100 Subject: [PATCH 123/182] Update core/mmu_unify/cva6_mmu.sv Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 448 ++++++++++++++++++------------------- 1 file changed, 224 insertions(+), 224 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 13c27d862c..55829439e5 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -121,231 +121,231 @@ module cva6_mmu logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; logic [VPN_LEN-1:0] vpn; logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; - } ; - - logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page - logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - - logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - pte_cva6_t [HYP_EXT:0] itlb_content ; - logic [PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - logic [riscv::GPLEN-1:0] itlb_gpaddr; - logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - - logic dtlb_lu_access; - pte_cva6_t [HYP_EXT:0] dtlb_content ; - logic [PT_LEVELS-2:0] dtlb_is_page; - logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; - logic dtlb_lu_hit; - logic [riscv::GPLEN-1:0] dtlb_gpaddr; - - logic shared_tlb_access; - logic shared_tlb_hit, itlb_req; - - // Assignments - - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES ( INSTR_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( enable_translation_i ), - .update_i ( update_itlb ), - .lu_access_i ( itlb_lu_access ), - .lu_asid_i ( itlb_mmu_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_gpaddr_o ( itlb_gpaddr ), - .lu_is_page_o ( itlb_is_page ), - .lu_hit_o ( itlb_lu_hit ) - ); - - cva6_tlb #( - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT(HYP_EXT), - .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 ), - .v_st_enbl_i ( en_ld_st_translation_i ), - .update_i ( update_dtlb ), - .lu_access_i ( dtlb_lu_access ), - .lu_asid_i ( dtlb_mmu_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_gpaddr_o ( dtlb_gpaddr ), - .lu_is_page_o ( dtlb_is_page ), - .lu_hit_o ( dtlb_lu_hit ) - ); - - - cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT(HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i,en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t(pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT(HYP_EXT), - .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 ), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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 ), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o ( update_vaddr ), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i ( hlvx_inst_i ), - // from CSR file - .satp_ppn_i (satp_ppn_i ), - .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_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 - // .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 - // ); + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; + }; - //----------------------- - // 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : + logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + pte_cva6_t [ HYP_EXT:0] itlb_content; + logic [ PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + logic [ riscv::GPLEN-1:0] itlb_gpaddr; + logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + + logic dtlb_lu_access; + pte_cva6_t [ HYP_EXT:0] dtlb_content; + logic [ PT_LEVELS-2:0] dtlb_is_page; + logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; + logic dtlb_lu_hit; + logic [ riscv::GPLEN-1:0] dtlb_gpaddr; + + logic shared_tlb_access; + logic shared_tlb_hit, itlb_req; + + // Assignments + + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (INSTR_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (enable_translation_i), + .update_i (update_itlb), + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), + .lu_is_page_o (itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (en_ld_st_translation_i), + .update_i (update_dtlb), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), + .lu_is_page_o (dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + + cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT (HYP_EXT), + .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), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_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), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o(update_vaddr), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i(hlvx_inst_i), + // from CSR file + .satp_ppn_i (satp_ppn_i), + .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_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 + // .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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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; From 820ceff54d4c3462bc7d77dc84614d5927e66b2a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:53:50 +0100 Subject: [PATCH 124/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 52 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 55829439e5..26c0cd975d 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -374,32 +374,32 @@ module cva6_mmu iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end - end + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end + end icache_areq_o.fetch_valid = 1'b0; From bffcde3bf83b1c802dbc519ef7506885dfc90a24 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:57:00 +0100 Subject: [PATCH 125/182] linting --- core/mmu_unify/cva6_mmu.sv | 202 ++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 26c0cd975d..2498559a36 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -382,136 +382,136 @@ module cva6_mmu // an error. if ((|enable_translation_i[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + 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 + if (HYP_EXT == 1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + end end - end - icache_areq_o.fetch_valid = 1'b0; + 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - end + end - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin + end else if (!pmp_instr_allow) begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end + end else begin + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end - end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end else begin - if (HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - end - end - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else begin - if(HYP_EXT==1) begin + end else begin + if (HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 }; end end end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end + end end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From a394c5896a7abd0d08533a66ffba9b364465964a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 12:59:06 +0100 Subject: [PATCH 126/182] linting --- core/mmu_unify/cva6_mmu.sv | 214 ++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 2498559a36..1e81990804 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -374,129 +374,76 @@ module cva6_mmu iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - end else if (iaccess_err[0]) begin - // throw a page fault - if(HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + end else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end + end - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end - end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + // we got an access error + end else if (iaccess_err[0]) begin + // throw a page fault + if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; - end else begin - if (HYP_EXT==1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - end - else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 }; - end end - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else begin + + end else if (!pmp_instr_allow) begin if(HYP_EXT==1) begin icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -505,13 +452,66 @@ module cva6_mmu end else begin icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end + end + end else begin + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end else begin + if (HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + }; + end + end + end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else begin + if(HYP_EXT==1) begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; + end end end end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From 80a0868ac8056578011c0030ef1ebbab3313cec9 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:00:46 +0100 Subject: [PATCH 127/182] linting --- core/mmu_unify/cva6_mmu.sv | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1e81990804..122b996521 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -400,9 +400,7 @@ module cva6_mmu }; end end - icache_areq_o.fetch_valid = 1'b0; - // --------- // ITLB Hit // -------- From 88a3dcb5d5c265c125990f3ffdfd79ab5c05fc79 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:03:49 +0100 Subject: [PATCH 128/182] linting --- 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 122b996521..3d4690b0fa 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -510,6 +510,7 @@ module cva6_mmu end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From 7fbb419b4492288360ac51921578ac10a8213f91 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:05:58 +0100 Subject: [PATCH 129/182] linting --- core/mmu_unify/cva6_mmu.sv | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 3d4690b0fa..e3ea21e955 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -384,20 +384,20 @@ module cva6_mmu // 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 if (HYP_EXT == 1) begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; end else begin - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; end end icache_areq_o.fetch_valid = 1'b0; @@ -510,7 +510,7 @@ module cva6_mmu end 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin From 5bfbac68ddddc78139349d4cd06b73e8ba18a58f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:13:05 +0100 Subject: [PATCH 130/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e3ea21e955..b7366ee5f6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -346,7 +346,7 @@ module cva6_mmu assign icache_areq_o.fetch_paddr[riscv::PLEN-1:PPNWMin+1] = // (|enable_translation_i[HYP_EXT:0]) ? // (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : - itlb_content[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 @@ -355,7 +355,7 @@ module cva6_mmu assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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)))]) : // + 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 @@ -375,7 +375,7 @@ module cva6_mmu || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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 From 9e44f5e6dbefca2be0f7c5bffd0bfea16ee08266 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:15:50 +0100 Subject: [PATCH 131/182] linting --- core/mmu_unify/cva6_mmu.sv | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index b7366ee5f6..3fee694546 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -383,7 +383,7 @@ module cva6_mmu if ((|enable_translation_i[HYP_EXT:0])) 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 - if (HYP_EXT == 1) begin + if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, @@ -392,13 +392,12 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - end else begin + else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; - end end icache_areq_o.fetch_valid = 1'b0; // --------- From cb892162358b707ee23de063a8c2d30032265ba1 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:18:10 +0100 Subject: [PATCH 132/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 3fee694546..fea6bc5dd3 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -382,7 +382,7 @@ module cva6_mmu // an error. if ((|enable_translation_i[HYP_EXT:0])) 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 + 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)) if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -398,7 +398,6 @@ module cva6_mmu {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1 }; - end icache_areq_o.fetch_valid = 1'b0; // --------- // ITLB Hit From 7515e6a4b165809f1294428da914db4ffb62024b Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:19:26 +0100 Subject: [PATCH 133/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index fea6bc5dd3..f9c8fcb952 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -374,8 +374,7 @@ module cva6_mmu iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; + if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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 From e8425a3b3cb07ae752a55f6afe13cfc58c6719ab Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:26:31 +0100 Subject: [PATCH 134/182] linting --- core/mmu_unify/cva6_mmu.sv | 55 +++++++++++++------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f9c8fcb952..29071dd4f2 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -393,9 +393,7 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 + riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 }; icache_areq_o.fetch_valid = 1'b0; // --------- @@ -404,7 +402,7 @@ module cva6_mmu // 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) begin + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, @@ -414,9 +412,9 @@ module cva6_mmu 1'b1 }; // we got an access error - end else if (iaccess_err[0]) begin + else if (iaccess_err[0]) // throw a page fault - if(HYP_EXT==1) begin + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, @@ -425,17 +423,12 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin + else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 + riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 }; - end - - end else if (!pmp_instr_allow) begin - if(HYP_EXT==1) begin + else if (!pmp_instr_allow) begin + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, @@ -444,12 +437,10 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin + else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 }; - end end end else begin // --------- @@ -459,7 +450,7 @@ module cva6_mmu if (ptw_active && walking_instr) begin icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, @@ -468,8 +459,8 @@ module cva6_mmu enable_translation_i[2*HYP_EXT], 1'b1 }; - end else begin - if (HYP_EXT==1) begin + else + if (HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, @@ -478,17 +469,14 @@ module cva6_mmu enable_translation_i[2*HYP_EXT], 1'b1 }; - end - else begin + else icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 }; - end - end end // TODO(moschn,zarubaf): What should the value of tval be in this case? - else begin - if(HYP_EXT==1) begin + else + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, @@ -497,13 +485,10 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - end - else begin + else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end - end end end end @@ -511,7 +496,7 @@ module cva6_mmu // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) begin + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN '(icache_areq_o.fetch_paddr)}, @@ -519,13 +504,11 @@ module cva6_mmu {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 - }; - end - else begin + }; + else 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 end From 040d32557b51827a259a2cfe631a1a4b485ff449 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:28:24 +0100 Subject: [PATCH 135/182] linting --- core/mmu_unify/cva6_mmu.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 29071dd4f2..e01a0bf27d 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -499,7 +499,7 @@ module cva6_mmu if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_o.fetch_paddr)}, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], From aaa4e10629126a70d21bf99dfa895c28f00e6b42 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:33:35 +0100 Subject: [PATCH 136/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index e01a0bf27d..ff648d9fe5 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -555,8 +555,7 @@ module cva6_mmu logic pmp_data_allow; assign lsu_paddr_o [11:0] = lsu_vaddr_q[0][11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); From c31f00d6ecb324a38333e5644d05969d826cdd34 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:38:25 +0100 Subject: [PATCH 137/182] linting --- core/mmu_unify/cva6_mmu.sv | 205 ++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 103 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index ff648d9fe5..1b75056dc6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -361,137 +361,136 @@ module cva6_mmu endgenerate - // The instruction interface is a simple request response interface - always_comb begin : instr_interface + // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - - if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) - if (HYP_EXT == 1) + 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + + if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::GPLEN{1'b0}}, + {{riscv::XLEN{1'b0}}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 + }; + 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; + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + else if (iaccess_err[0]) + // throw a page fault + if(HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, + {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - else + else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 - }; - 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) + riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 + }; + else if (!pmp_instr_allow) + if(HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - itlb_gpaddr[riscv::GPLEN-1:0], + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - else if (iaccess_err[0]) - // throw a page fault - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 - }; - else if (!pmp_instr_allow) begin - if(HYP_EXT==1) + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + }; + end else begin + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], 1'b1 }; else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; - end - end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) + if (HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - if (HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 - }; - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 }; end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + }; end 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 From 3bd12fecf8ffbe62bb0363f997b642623c54a90c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:40:03 +0100 Subject: [PATCH 138/182] linting --- core/mmu_unify/cva6_mmu.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1b75056dc6..1862fc8901 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -387,7 +387,7 @@ module cva6_mmu riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, {riscv::GPLEN{1'b0}}, - {{riscv::XLEN{1'b0}}}, + {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; From f2c9357e0c144a160bcc44d46093d9fa18f09e24 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:41:01 +0100 Subject: [PATCH 139/182] Update core/mmu_unify/cva6_mmu.sv Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 1862fc8901..d2d046e389 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -363,7 +363,7 @@ module cva6_mmu // The instruction interface is a simple request response interface always_comb begin : instr_interface - // MMU disabled: just pass through + // 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: From d7a50ce32c415bc87d0e483994d4c928832875bc Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:41:47 +0100 Subject: [PATCH 140/182] Update core/mmu_unify/cva6_mmu.sv Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d2d046e389..78da21f5bf 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -374,7 +374,8 @@ module cva6_mmu iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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 From 2c457226997ebc9bcea3057415f412fe1d7ce505 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:43:08 +0100 Subject: [PATCH 141/182] linting --- core/mmu_unify/cva6_mmu.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 78da21f5bf..bdf04490a0 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -381,7 +381,7 @@ module cva6_mmu // AXI decode error), or when PTW performs walk due to ITLB miss and raises // an error. if ((|enable_translation_i[HYP_EXT:0])) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal + // 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)) if (HYP_EXT == 1) icache_areq_o.fetch_exception = { From 1b54f2e7cea1dee28deb368d7b26dc0d42129e42 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:44:42 +0100 Subject: [PATCH 142/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index bdf04490a0..55f369e384 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -397,9 +397,9 @@ module cva6_mmu riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 }; 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; From 04e6db6135a46b82cf2ed88d766dbf677f5d64dc Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:48:49 +0100 Subject: [PATCH 143/182] linting --- core/mmu_unify/cva6_mmu.sv | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 55f369e384..b69b055349 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -394,7 +394,9 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 + riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, + icache_areq_i.fetch_vaddr}, + 1'b1 }; icache_areq_o.fetch_valid = 1'b0; // ---------// @@ -426,7 +428,9 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},1'b1 + riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, + icache_areq_i.fetch_vaddr}, + 1'b1 }; else if (!pmp_instr_allow) if(HYP_EXT==1) @@ -440,7 +444,9 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 + riscv::INSTR_ACCESS_FAULT, + riscv::XLEN '(icache_areq_i.fetch_vaddr), + 1'b1 }; end else begin // --------- @@ -471,7 +477,9 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1 + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, + 1'b1 }; end // TODO(moschn,zarubaf): What should the value of tval be in this case? From 94d06af0e1dc658400cb70580bc5ba29fb627282 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:53:37 +0100 Subject: [PATCH 144/182] linting --- core/mmu_unify/cva6_mmu.sv | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index b69b055349..7183be409a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -386,7 +386,7 @@ module cva6_mmu if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -394,8 +394,8 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, - icache_areq_i.fetch_vaddr}, + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, 1'b1 }; icache_areq_o.fetch_valid = 1'b0; @@ -408,7 +408,7 @@ module cva6_mmu if (HYP_EXT==1 && iaccess_err[HYP_EXT]) icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, itlb_gpaddr[riscv::GPLEN-1:0], {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -420,7 +420,7 @@ module cva6_mmu if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -436,7 +436,7 @@ module cva6_mmu if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], @@ -459,7 +459,7 @@ module cva6_mmu if (HYP_EXT==1 && ptw_error[HYP_EXT]) icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {{riscv::XLEN'(update_vaddr)}, ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), enable_translation_i[2*HYP_EXT], @@ -469,7 +469,7 @@ module cva6_mmu if (HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {{riscv::XLEN'(update_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[2*HYP_EXT], @@ -478,7 +478,7 @@ module cva6_mmu else icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, + {{riscv::XLEN'(update_vaddr)}, 1'b1 }; end @@ -487,7 +487,7 @@ module cva6_mmu if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, + {{riscv::XLEN'(update_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], From b561139731f70eabaa95bee6406219db610a9f56 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 13:59:40 +0100 Subject: [PATCH 145/182] LINTING --- core/mmu_unify/cva6_mmu.sv | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 7183be409a..2fcf8ecbc6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -501,24 +501,24 @@ module cva6_mmu 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - 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 + // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + 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}); From d624161b96843e74bc415c37d935d9bad1843d44 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:01:39 +0100 Subject: [PATCH 146/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 2fcf8ecbc6..4d44c71e90 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -495,7 +495,9 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 + riscv::INSTR_ACCESS_FAULT, + ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], + 1'b1 }; end end From fd8f7a6d9d69bcdf5b9cf31beffbdc906b0bec46 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:45:22 +0100 Subject: [PATCH 147/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 4d44c71e90..c89844785b 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -494,11 +494,7 @@ module cva6_mmu 1'b1 }; else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], - 1'b1 - }; + icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end end From f456bd7be76e92d904aecc72f69635e201d654d7 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:47:15 +0100 Subject: [PATCH 148/182] linting --- core/mmu_unify/cva6_mmu.sv | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c89844785b..1694c21326 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -449,54 +449,54 @@ module cva6_mmu 1'b1 }; end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - if (HYP_EXT==1) + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_GUEST_PAGE_FAULT, {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), enable_translation_i[2*HYP_EXT], 1'b1 }; else + if (HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN'(update_vaddr)}, + 1'b1 + }; + end + // TODO(moschn,zarubaf): What should the value of tval be in this case? + else + if(HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], 1'b1 }; + else + icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - end end // if it didn't match any execute region throw an `Instruction Access Fault` From f144f7d8e70b96b316bc17a0bbd234271fdc1f02 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:49:43 +0100 Subject: [PATCH 149/182] linting --- 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 1694c21326..744a08764f 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -497,6 +497,7 @@ module cva6_mmu icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end + end // if it didn't match any execute region throw an `Instruction Access Fault` From 92f990d9dfce70433a1c23ea86984e0ce2910cd7 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:52:57 +0100 Subject: [PATCH 150/182] linting --- core/mmu_unify/cva6_mmu.sv | 39 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 744a08764f..463578b977 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -496,27 +496,26 @@ module cva6_mmu else icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end - end - - end + 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - 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 + // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + 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 From 4073824330155dee287cb1e559f66f17b4900b07 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 14:57:39 +0100 Subject: [PATCH 151/182] linting --- core/mmu_unify/cva6_mmu.sv | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 463578b977..0107e00221 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -499,23 +499,23 @@ module cva6_mmu 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - 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 + // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + 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 From fb11c58966f91d85a90e7ce61993f365fa11caea Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 15:52:36 +0100 Subject: [PATCH 152/182] linting --- core/mmu_unify/cva6_mmu.sv | 76 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 0107e00221..c4fdcdd1d0 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -449,55 +449,53 @@ module cva6_mmu 1'b1 }; end else begin - // --------- - // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) + // --------- + // 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[0] | ptw_access_exception; + if (ptw_error[0]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {{riscv::XLEN'(update_vaddr)}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + if (HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, enable_translation_i[2*HYP_EXT], 1'b1 }; else - if (HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - 1'b1 - }; - end - // TODO(moschn,zarubaf): What should the value of tval be in this case? - else - if(HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], 1'b1 }; - else - icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end - end - end + end else + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {{riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end + 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 From daf8052dacfe5270ad3e8ee061ab3ed1300d859d Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 15:55:34 +0100 Subject: [PATCH 153/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c4fdcdd1d0..35d049d08e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -481,7 +481,7 @@ module cva6_mmu {{riscv::XLEN'(update_vaddr)}, 1'b1 }; - end else + end else begin if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -493,6 +493,7 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + end end end end From a457a7b8f1233d4168685a940d9e5b98a0215110 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:01:36 +0100 Subject: [PATCH 154/182] linting --- core/mmu_unify/cva6_mmu.sv | 66 ++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 35d049d08e..c50c158093 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -448,52 +448,50 @@ module cva6_mmu riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 }; - end else begin + end else if (ptw_active && walking_instr) begin // --------- // 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[0] | ptw_access_exception; - if (ptw_error[0]) begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; + if (ptw_error[0]) + if (HYP_EXT==1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + if (HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, enable_translation_i[2*HYP_EXT], 1'b1 }; else - if (HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN'(update_vaddr)}, - 1'b1 - }; - end else begin - if(HYP_EXT==1) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {{riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, 1'b1 }; - else - icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end + else begin + if(HYP_EXT==1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; end end end From 917f5af581bbe7e2707118b23b39d134b3e815e6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:05:38 +0100 Subject: [PATCH 155/182] linting --- core/mmu_unify/cva6_mmu.sv | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index c50c158093..63b582728a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -464,8 +464,7 @@ module cva6_mmu enable_translation_i[2*HYP_EXT], 1'b1 }; - else - if (HYP_EXT==1) + else if (HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, @@ -490,8 +489,11 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - else - icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + else icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], + 1'b1 + }; end end end From 95b21203979548d6114df6ae5fd96810517bc424 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:08:17 +0100 Subject: [PATCH 156/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 63b582728a..53e5bc8f21 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -479,7 +479,7 @@ module cva6_mmu {riscv::XLEN'(update_vaddr)}, 1'b1 }; - else begin + else if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -494,7 +494,6 @@ module cva6_mmu ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end end end From a25f9dc755733df1cadbb6bf4f057e4d5776541e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:10:26 +0100 Subject: [PATCH 157/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 53e5bc8f21..0ddf78a696 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -493,7 +493,7 @@ module cva6_mmu riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 - }; + }; end end @@ -506,7 +506,7 @@ module cva6_mmu {riscv::XLEN'(icache_areq_o.fetch_paddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; else From 21acb14cb80cfb8f65d189189da908fc94957e5f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:14:42 +0100 Subject: [PATCH 158/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 0ddf78a696..d93ff2cd61 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -479,8 +479,7 @@ module cva6_mmu {riscv::XLEN'(update_vaddr)}, 1'b1 }; - else - if(HYP_EXT==1) + else if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(update_vaddr)}, From 5e57510802e9d492e25c67a578adbd93e2f593df Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:17:14 +0100 Subject: [PATCH 159/182] linting --- core/mmu_unify/cva6_mmu.sv | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d93ff2cd61..089fdd1459 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -498,7 +498,7 @@ module cva6_mmu // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) begin + if ((!match_any_execute_region && (!ptw_error[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -510,9 +510,11 @@ module cva6_mmu }; else 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 + 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 From f79055e58045b957c76787a898c4ed81e9c77e21 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:43:56 +0100 Subject: [PATCH 160/182] linting --- core/mmu_unify/cva6_mmu.sv | 421 ++++++++++++++++++------------------- 1 file changed, 209 insertions(+), 212 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 089fdd1459..6a2fed3575 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -34,8 +34,8 @@ module cva6_mmu input logic clk_i, input logic rst_ni, input logic flush_i, - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st // IF interface input icache_arsp_t icache_areq_i, output icache_areq_t icache_areq_o, @@ -53,7 +53,7 @@ module cva6_mmu output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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 lsu_dtlb_hit_o, // sent in same cycle as the request if translation hits in 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 @@ -90,19 +90,18 @@ module cva6_mmu genvar b; generate - for (b = 0; b < HYP_EXT + 1; b++) begin - assign dtlb_mmu_asid_i[b] = b==0 ? - ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; + for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid + assign dtlb_mmu_asid_i[b] = b==0 ? + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; assign itlb_mmu_asid_i[b] = b==0 ? - (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; end endgenerate // 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; @@ -116,7 +115,6 @@ module cva6_mmu }; localparam type tlb_update_cva6_t = struct packed { - // typedef struct packed { logic valid; logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; logic [VPN_LEN-1:0] vpn; @@ -340,7 +338,7 @@ module cva6_mmu //----------------------- logic match_any_execute_region; logic pmp_instr_allow; - localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1; + localparam int 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] = // @@ -351,7 +349,7 @@ module cva6_mmu genvar a; generate - for (a = 0; a < PT_LEVELS - 1; a++) begin + for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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)))]: @@ -383,7 +381,7 @@ module cva6_mmu if ((|enable_translation_i[HYP_EXT:0])) 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)) - if (HYP_EXT == 1) + if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, @@ -392,7 +390,7 @@ module cva6_mmu enable_translation_i[HYP_EXT*2], 1'b1 }; - else + else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, @@ -405,7 +403,7 @@ module cva6_mmu // 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) + if (HYP_EXT==1 && iaccess_err[HYP_EXT]) icache_areq_o.fetch_exception = { riscv::INSTR_GUEST_PAGE_FAULT, {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, @@ -415,9 +413,9 @@ module cva6_mmu 1'b1 }; // we got an access error - else if (iaccess_err[0]) + else if (iaccess_err[0]) // throw a page fault - if(HYP_EXT==1) + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_PAGE_FAULT, {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, @@ -433,7 +431,7 @@ module cva6_mmu 1'b1 }; else if (!pmp_instr_allow) - if(HYP_EXT==1) + if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, @@ -445,9 +443,9 @@ module cva6_mmu else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - riscv::XLEN '(icache_areq_i.fetch_vaddr), + riscv::XLEN '(icache_areq_i.fetch_vaddr), 1'b1 - }; + }; end else if (ptw_active && walking_instr) begin // --------- // ITLB Miss @@ -475,7 +473,7 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, 1'b1 }; @@ -493,12 +491,12 @@ module cva6_mmu ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end + 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) + if ((!match_any_execute_region && (!ptw_error[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) if(HYP_EXT==1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -507,14 +505,13 @@ module cva6_mmu {riscv::XLEN{1'b0}}, enable_translation_i[2*HYP_EXT], 1'b1 - }; + }; else 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]), + 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 // check for execute flag on memory @@ -562,18 +559,18 @@ module cva6_mmu assign lsu_paddr_o [11:0] = lsu_vaddr_q[0][11:0]; assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: - dtlb_content[0].ppn[11:0]) : // + dtlb_content[0].ppn[11:0]) : // lsu_vaddr_n[0][23:12]; genvar i; generate - for (i = 0; i < PT_LEVELS - 1; i++) begin + for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: @@ -588,10 +585,10 @@ module cva6_mmu lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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[HYP_EXT:0] && !misaligned_ex_q.valid) ? + if (riscv::IS_XLEN64) begin : gen_ppn_64 + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; end @@ -628,41 +625,149 @@ module cva6_mmu daccess_err[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); - - if(HYP_EXT==1) - daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; - // translation is enabled and no misaligned exception occurred - if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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) + if(HYP_EXT==1) + daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {riscv::XLEN '(lsu_paddr_o)}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else 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 - // this is a store + // this is a load + end else begin + if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + end else begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -672,46 +777,26 @@ module cva6_mmu else begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {riscv::XLEN '(lsu_paddr_o)}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, 1'b1 }; end - else 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 - - // this is a load end else begin - if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin + if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin lsu_exception_o = { riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - // check for sufficient access privileges - throw a page fault if necessary - end else if (daccess_err[0]) begin + end else begin if(HYP_EXT==1) begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -721,139 +806,19 @@ module cva6_mmu else begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, 1'b1 }; end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 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 else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error[0]) 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 - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - end else begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - 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 - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][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 + + 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 if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else - 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 - if(HYP_EXT==1) begin lsu_exception_o = { riscv::LD_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, @@ -864,11 +829,43 @@ module cva6_mmu }; 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}; + lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][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 + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end + else + 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 + if(HYP_EXT==1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 #( From 0bb6cac213ce614cff1564055977fa750d10165a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:52:27 +0100 Subject: [PATCH 161/182] linting --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6a2fed3575..2761fcf576 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -34,7 +34,7 @@ module cva6_mmu input logic clk_i, input logic rst_ni, input logic flush_i, - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st // IF interface input icache_arsp_t icache_areq_i, @@ -53,7 +53,7 @@ module cva6_mmu output logic csr_hs_ld_st_inst_o, // hyp load store instruction // 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 same cycle as the request if translation hits in DTLB + output logic lsu_dtlb_hit_o, // sent in same cycle as the request if translation hits in 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 @@ -491,7 +491,7 @@ module cva6_mmu ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1 }; - end + end end // if it didn't match any execute region throw an `Instruction Access Fault` From c26126169dc9ba27fac9d5f70eb906d71cf0515d Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 16:58:15 +0100 Subject: [PATCH 162/182] verible-formatter-mmu --- core/mmu_unify/cva6_mmu.sv | 1730 ++++++++++++++++++------------------ 1 file changed, 866 insertions(+), 864 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 2761fcf576..49b4616ac4 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -19,895 +19,897 @@ module cva6_mmu - import ariane_pkg::*; +import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - 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 ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now - // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction - // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - - input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] - 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 [HYP_EXT*2:0] flush_tlb_i, + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now + // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction + // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + + input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; + +genvar b; +generate + for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid + assign dtlb_mmu_asid_i[b] = b==0 ? + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + assign itlb_mmu_asid_i[b] = b==0 ? + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + end +endgenerate + +// memory management, pte for cva6 +localparam type pte_cva6_t = 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 { + logic valid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; +}; + +logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page +logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + +logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +pte_cva6_t [ HYP_EXT:0] itlb_content; +logic [ PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; +logic [ riscv::GPLEN-1:0] itlb_gpaddr; +logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + +logic dtlb_lu_access; +pte_cva6_t [ HYP_EXT:0] dtlb_content; +logic [ PT_LEVELS-2:0] dtlb_is_page; +logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; +logic dtlb_lu_hit; +logic [ riscv::GPLEN-1:0] dtlb_gpaddr; + +logic shared_tlb_access; +logic shared_tlb_hit, itlb_req; + +// Assignments + +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (INSTR_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (enable_translation_i), + .update_i (update_itlb), + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), + .lu_is_page_o (itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (en_ld_st_translation_i), + .update_i (update_dtlb), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), + .lu_is_page_o (dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + + +cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT (HYP_EXT), + .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), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .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), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o(update_vaddr), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i(hlvx_inst_i), + // from CSR file + .satp_ppn_i (satp_ppn_i), + .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 -); - logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; - logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; - - genvar b; - generate - for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid - assign dtlb_mmu_asid_i[b] = b==0 ? - ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? - (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - end - endgenerate - - // memory management, pte for cva6 - localparam type pte_cva6_t = 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 { - logic valid; - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; - logic [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; - }; - - logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page - logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - - logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - pte_cva6_t [ HYP_EXT:0] itlb_content; - logic [ PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - logic [ riscv::GPLEN-1:0] itlb_gpaddr; - logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - - logic dtlb_lu_access; - pte_cva6_t [ HYP_EXT:0] dtlb_content; - logic [ PT_LEVELS-2:0] dtlb_is_page; - logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; - logic dtlb_lu_hit; - logic [ riscv::GPLEN-1:0] dtlb_gpaddr; - - logic shared_tlb_access; - logic shared_tlb_hit, itlb_req; - - // Assignments - - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (INSTR_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (enable_translation_i), - .update_i (update_itlb), - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), - .lu_is_page_o (itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (en_ld_st_translation_i), - .update_i (update_dtlb), - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), - .lu_is_page_o (dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - - cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT (HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT (HYP_EXT), - .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), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o(update_vaddr), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i(hlvx_inst_i), - // from CSR file - .satp_ppn_i (satp_ppn_i), - .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_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 - // .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 int 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 + .pmpcfg_i (pmpcfg_i), + .pmpaddr_i (pmpaddr_i), + .bad_paddr_o(ptw_bad_paddr) - 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); +); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) - if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - 1'b1 - }; - 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; - if (HYP_EXT==1 && iaccess_err[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - // we got an access error - else if (iaccess_err[0]) - // throw a page fault - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {riscv::XLEN '(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT,{{riscv::XLEN - riscv::VLEN{1'b0}}, - icache_areq_i.fetch_vaddr}, - 1'b1 - }; - else if (!pmp_instr_allow) - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - riscv::XLEN '(icache_areq_i.fetch_vaddr), - 1'b1 - }; - end else if (ptw_active && walking_instr) begin - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; - if (ptw_error[0]) - if (HYP_EXT==1 && ptw_error[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else if (HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - 1'b1 - }; - else if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], - 1'b1 - }; - end - end +// 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_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 +// .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 int 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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 + + for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 - // 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) - if(HYP_EXT==1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; +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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, 1'b1 + }; + 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; + if (HYP_EXT == 1 && iaccess_err[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + else if (iaccess_err[0]) + // throw a page fault + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; else - 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 - }; + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + else if (!pmp_instr_allow) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 + }; + end else if (ptw_active && walking_instr) begin + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; + if (ptw_error[0]) + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, 1'b1 + }; + else if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + ptw_bad_paddr[0][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}); - // assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension - .PLEN ( riscv::PLEN ), - .PMP_LEN ( riscv::PLEN - 2 ), - .NR_ENTRIES ( CVA6Cfg.NrPMPEntries ) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension - ) 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 [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; - logic hs_ld_st_inst_n, hs_ld_st_inst_q; - pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: - dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][23:12]; - - genvar i; - generate - - for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: - dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? - lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): - lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; - 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + 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 - endgenerate +// 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} +); +// assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now + +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension +) 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) +); - // 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[0] = lsu_vaddr_i; - lsu_tinst_n = lsu_tinst_i; +//----------------------- +// Data Interface +//----------------------- +logic [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; +logic hs_ld_st_inst_n, hs_ld_st_inst_q; +pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][23:12]; + +genvar i; +generate + + for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; + end - lsu_req_n = lsu_req_i; - hs_ld_st_inst_n = hs_ld_st_inst_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; +endgenerate - if (HYP_EXT == 1) begin - lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; - end +// 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[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_i; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& - ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); - - if(HYP_EXT==1) - daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; - // translation is enabled and no misaligned exception occurred - if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {riscv::XLEN '(lsu_paddr_o)}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else 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 - - // this is a load - end else begin - if (HYP_EXT==1 && daccess_err[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - // check for sufficient access privileges - throw a page fault if necessary - end else if (daccess_err[0]) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 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 else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error[0]) 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 - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - end else begin - if (HYP_EXT==1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - end - end + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_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; - 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 - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else begin - lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; - end - end + if (HYP_EXT == 1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + end + + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); + + if (HYP_EXT == 1) daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {riscv::XLEN'(lsu_paddr_o)}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else 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 - 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 + + // this is a load + end else begin + if (HYP_EXT == 1 && daccess_err[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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 - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end - else - lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], 1'b1}; + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; end else begin - if(HYP_EXT==1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}},update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 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 else begin + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; end + end end - end -end - - // Load/store PMP check - pmp #( - .CVA6Cfg (CVA6Cfg), // COMMENT IN HYPERVISOR EXTENSION - .PLEN ( riscv::PLEN ), - .PMP_LEN ( riscv::PLEN - 2 ), - .NR_ENTRIES ( CVA6Cfg.NrPMPEntries ) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION - ) 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_tinst_q <= '0; - hs_ld_st_inst_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 (ptw_access_exception) begin + // an error makes the translation valid + lsu_valid_o = 1'b1; + // the page table walker can only throw page faults + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; end else begin - lsu_vaddr_q <= lsu_vaddr_n; - lsu_tinst_q <= lsu_tinst_n; - hs_ld_st_inst_q <= hs_ld_st_inst_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, + ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], + 1'b1 + }; end + end end -endmodule + 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 + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else + 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 + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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), // COMMENT IN HYPERVISOR EXTENSION + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION +) 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_tinst_q <= '0; + hs_ld_st_inst_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_tinst_q <= lsu_tinst_n; + hs_ld_st_inst_q <= hs_ld_st_inst_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 9441263f0c258dab72ba42912b07118d929495c6 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:01:57 +0100 Subject: [PATCH 163/182] linting --- core/mmu_unify/cva6_mmu.sv | 1540 ++++++++++++++++++------------------ 1 file changed, 770 insertions(+), 770 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 49b4616ac4..f3fa97154e 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -19,403 +19,371 @@ module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - 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 ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now - // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction - // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - - input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] - 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 [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; -logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; - -genvar b; -generate - for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid - assign dtlb_mmu_asid_i[b] = b==0 ? - ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? - (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - end -endgenerate - -// memory management, pte for cva6 -localparam type pte_cva6_t = 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 { - logic valid; - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; - logic [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; -}; - -logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page -logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - -logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - -logic itlb_lu_access; -pte_cva6_t [ HYP_EXT:0] itlb_content; -logic [ PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; -logic [ riscv::GPLEN-1:0] itlb_gpaddr; -logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - -logic dtlb_lu_access; -pte_cva6_t [ HYP_EXT:0] dtlb_content; -logic [ PT_LEVELS-2:0] dtlb_is_page; -logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; -logic dtlb_lu_hit; -logic [ riscv::GPLEN-1:0] dtlb_gpaddr; - -logic shared_tlb_access; -logic shared_tlb_hit, itlb_req; - -// Assignments - -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (INSTR_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (enable_translation_i), - .update_i (update_itlb), - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), - .lu_is_page_o (itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); - -cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (en_ld_st_translation_i), - .update_i (update_dtlb), - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), - .lu_is_page_o (dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); - - -cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT (HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now + // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction + // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + + input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] 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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT (HYP_EXT), - .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), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_translation_i), - - .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), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o(update_vaddr), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i(hlvx_inst_i), - // from CSR file - .satp_ppn_i (satp_ppn_i), - .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 ); + logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; + + genvar b; + generate + for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid + assign dtlb_mmu_asid_i[b] = b==0 ? + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + assign itlb_mmu_asid_i[b] = b==0 ? + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + end + endgenerate + + // memory management, pte for cva6 + localparam type pte_cva6_t = 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 { + logic valid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; + }; + + logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + pte_cva6_t [ HYP_EXT:0] itlb_content; + logic [ PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + logic [ riscv::GPLEN-1:0] itlb_gpaddr; + logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + + logic dtlb_lu_access; + pte_cva6_t [ HYP_EXT:0] dtlb_content; + logic [ PT_LEVELS-2:0] dtlb_is_page; + logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; + logic dtlb_lu_hit; + logic [ riscv::GPLEN-1:0] dtlb_gpaddr; + + logic shared_tlb_access; + logic shared_tlb_hit, itlb_req; + + // Assignments + + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (INSTR_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (enable_translation_i), + .update_i (update_itlb), + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), + .lu_is_page_o (itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (en_ld_st_translation_i), + .update_i (update_dtlb), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), + .lu_is_page_o (dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + + cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT (HYP_EXT), + .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), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_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), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o(update_vaddr), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i(hlvx_inst_i), + // from CSR file + .satp_ppn_i (satp_ppn_i), + .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_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 + // .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 int 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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 + + for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 -// 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_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 -// .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 int 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 + 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); -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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) - if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, 1'b1 - }; - 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; - if (HYP_EXT == 1 && iaccess_err[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - itlb_gpaddr[riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - // we got an access error - else if (iaccess_err[0]) - // throw a page fault + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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)) if (HYP_EXT == 1) icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, @@ -424,346 +392,270 @@ always_comb begin : instr_interface }; else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 + riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, 1'b1 }; - else if (!pmp_instr_allow) - if (HYP_EXT == 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; + if (HYP_EXT == 1 && iaccess_err[HYP_EXT]) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_GUEST_PAGE_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, + itlb_gpaddr[riscv::GPLEN-1:0], {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 - }; - end else if (ptw_active && walking_instr) begin - // --------- - // ITLB Miss - // --------- - // watch out for exceptions happening during walking the page table - icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; - if (ptw_error[0]) - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; + // we got an access error + else if (iaccess_err[0]) + // throw a page fault + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, + 1'b1 + }; + else if (!pmp_instr_allow) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 + }; + end else if (ptw_active && walking_instr) begin + // --------- + // ITLB Miss + // --------- + // watch out for exceptions happening during walking the page table + icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; + if (ptw_error[0]) + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, 1'b1 + }; else if (HYP_EXT == 1) icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(update_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], + enable_translation_i[HYP_EXT*2], 1'b1 }; else icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, 1'b1 + riscv::INSTR_ACCESS_FAULT, + ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN>riscv::VLEN)?(riscv::PLEN-riscv::VLEN) : 0], + 1'b1 }; - else if (HYP_EXT == 1) + 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) + if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(update_vaddr)}, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; else icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN>riscv::VLEN)?(riscv::PLEN-riscv::VLEN) : 0], + 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) - if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - 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 - -// 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} -); -// assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now - -// Instruction fetch -pmp #( - .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension -) 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} + ); + // assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now + + // Instruction fetch + pmp #( + .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension + ) 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 [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; + logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; + logic hs_ld_st_inst_n, hs_ld_st_inst_q; + pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; + assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + + assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][23:12]; + + genvar i; + generate + + for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; + end -//----------------------- -// Data Interface -//----------------------- -logic [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; -logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; -logic hs_ld_st_inst_n, hs_ld_st_inst_q; -pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; -assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - -assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: - dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][23:12]; - -genvar i; -generate - - for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: - dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? - lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): - lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; - end + endgenerate -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[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_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[0] = lsu_vaddr_i; - lsu_tinst_n = lsu_tinst_i; + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_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_req_n = lsu_req_i; - hs_ld_st_inst_n = hs_ld_st_inst_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; + if (HYP_EXT == 1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + end - if (HYP_EXT == 1) begin - lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; - end + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_q; + pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ; - lsu_valid_o = lsu_req_q; - lsu_exception_o = misaligned_ex_q; - csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& - ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); - - if (HYP_EXT == 1) daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; - // translation is enabled and no misaligned exception occurred - if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {riscv::XLEN'(lsu_paddr_o)}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else 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 + // 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; - // this is a load - end else begin - if (HYP_EXT == 1 && daccess_err[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - // check for sufficient access privileges - throw a page fault if necessary - end else if (daccess_err[0]) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 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 else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error[0]) begin - // an error makes the translation valid + // Check if the User flag is set, then we may only access it in supervisor mode + // if SUM is enabled + daccess_err[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); + + if (HYP_EXT == 1) daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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 - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + // check if the page is write-able and we are not violating privileges + // also check if the dirty flag is set + if(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else begin + end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin if (HYP_EXT == 1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -772,26 +664,47 @@ always_comb begin : data_interface end else begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {riscv::XLEN'(lsu_paddr_o)}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else 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 + + // this is a load end else begin - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + if (HYP_EXT == 1 && daccess_err[HYP_EXT]) begin lsu_exception_o = { riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else begin + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin if (HYP_EXT == 1) begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -800,18 +713,142 @@ always_comb begin : data_interface end else begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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 + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; + end else begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end + end + end else begin + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + 1'b1 + }; + end end 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 + 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 + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + ptw_bad_paddr[0][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 + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else + 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 if (HYP_EXT == 1) begin lsu_exception_o = { riscv::LD_ACCESS_FAULT, @@ -824,92 +861,55 @@ always_comb begin : data_interface end else begin lsu_exception_o = { riscv::LD_ACCESS_FAULT, - ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN > riscv::VLEN) ? (riscv::PLEN - riscv::VLEN) : 0], + lsu_paddr_o[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 - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else - 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 - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 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), // COMMENT IN HYPERVISOR EXTENSION - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION -) 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_tinst_q <= '0; - hs_ld_st_inst_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_tinst_q <= lsu_tinst_n; - hs_ld_st_inst_q <= hs_ld_st_inst_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), // COMMENT IN HYPERVISOR EXTENSION + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION + ) 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_tinst_q <= '0; + hs_ld_st_inst_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_tinst_q <= lsu_tinst_n; + hs_ld_st_inst_q <= hs_ld_st_inst_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 ff786aee43e5ebaa995258d39c606d60b74c3ecf Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:04:20 +0100 Subject: [PATCH 164/182] linting --- core/mmu_unify/cva6_mmu.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f3fa97154e..8b9bc13ede 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -356,7 +356,6 @@ module cva6_mmu 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 - endgenerate // The instruction interface is a simple request response interface From c8cbc80bc1f7df78581a3e33fe5e8bc324a27190 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:06:38 +0100 Subject: [PATCH 165/182] linting --- 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 8b9bc13ede..4f8fca37d0 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -368,8 +368,9 @@ module cva6_mmu // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && // + (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; From fd6147e83f274bfc9fd82409c5bd44124507299e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:08:22 +0100 Subject: [PATCH 166/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 4f8fca37d0..ce198d3965 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -369,8 +369,8 @@ module cva6_mmu icache_areq_o.fetch_exception = '0; // Check whether we are allowed to access this memory region from a fetch perspective iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && // - (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; From 2fe25465b4f2f53bfa39027193e0cc8491723a74 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:10:23 +0100 Subject: [PATCH 167/182] linting --- core/mmu_unify/cva6_mmu.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index ce198d3965..a3f4749497 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -380,7 +380,7 @@ module cva6_mmu // an error. if ((|enable_translation_i[HYP_EXT:0])) 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)) + 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 if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -394,6 +394,7 @@ module cva6_mmu 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; // ---------// // ITLB Hit From 68404050608dc8306ec6a5f6ff42d3c41b338207 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:11:18 +0100 Subject: [PATCH 168/182] Update core/mmu_unify/cva6_mmu.sv Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index a3f4749497..c43d659985 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -368,9 +368,9 @@ module cva6_mmu // 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT==0) && // - (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + iaccess_err[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT == 0) && // + (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); if (HYP_EXT == 1) iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].u; From f1c20f6107cfa773b3c323fe85e26459038f87f2 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:13:12 +0100 Subject: [PATCH 169/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index a3f4749497..86ec40632c 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -444,9 +444,9 @@ module cva6_mmu riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 }; end else if (ptw_active && walking_instr) begin - // --------- + // ---------// // ITLB Miss - // --------- + // ---------// // watch out for exceptions happening during walking the page table icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; if (ptw_error[0]) From 08dd0af921c4d58811138536c5ea80c7d0e07298 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:20:49 +0100 Subject: [PATCH 170/182] linting --- core/mmu_unify/cva6_mmu.sv | 1546 ++++++++++++++++++------------------ 1 file changed, 773 insertions(+), 773 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 9ae031a4b6..8266d99f91 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -19,371 +19,404 @@ module cva6_mmu - import ariane_pkg::*; +import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - 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 ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now - // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction - // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - - input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] - 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 [HYP_EXT*2:0] flush_tlb_i, + input logic clk_i, + input logic rst_ni, + input logic flush_i, + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now + // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction + // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + + input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; +logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; + +genvar b; +generate + for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid + assign dtlb_mmu_asid_i[b] = b==0 ? + ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + assign itlb_mmu_asid_i[b] = b==0 ? + (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): + asid_i[HYP_EXT*2]; + end +endgenerate + +// memory management, pte for cva6 +localparam type pte_cva6_t = 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 { + logic valid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; +}; + +logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page +logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception +logic ptw_access_exception; // PTW threw an access exception (PMPs) +logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + +logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + +tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + +logic itlb_lu_access; +pte_cva6_t [ HYP_EXT:0] itlb_content; +logic [ PT_LEVELS-2:0] itlb_is_page; +logic itlb_lu_hit; +logic [ riscv::GPLEN-1:0] itlb_gpaddr; +logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + +logic dtlb_lu_access; +pte_cva6_t [ HYP_EXT:0] dtlb_content; +logic [ PT_LEVELS-2:0] dtlb_is_page; +logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; +logic dtlb_lu_hit; +logic [ riscv::GPLEN-1:0] dtlb_gpaddr; + +logic shared_tlb_access; +logic shared_tlb_hit, itlb_req; + +// Assignments + +assign itlb_lu_access = icache_areq_i.fetch_req; +assign dtlb_lu_access = lsu_req_i; + + +cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (INSTR_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (enable_translation_i), + .update_i (update_itlb), + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), + .lu_is_page_o (itlb_is_page), + .lu_hit_o (itlb_lu_hit) +); + +cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (en_ld_st_translation_i), + .update_i (update_dtlb), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), + .lu_is_page_o (dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) +); + + +cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT (HYP_EXT), + .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), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_translation_i), + + .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), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o(update_vaddr), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i(hlvx_inst_i), + // from CSR file + .satp_ppn_i (satp_ppn_i), + .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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; - logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; - - genvar b; - generate - for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid - assign dtlb_mmu_asid_i[b] = b==0 ? - ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? - (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): - asid_i[HYP_EXT*2]; - end - endgenerate - - // memory management, pte for cva6 - localparam type pte_cva6_t = 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 { - logic valid; - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; - logic [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; - }; - - logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page - logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception - logic ptw_access_exception; // PTW threw an access exception (PMPs) - logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - - logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - - tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - - logic itlb_lu_access; - pte_cva6_t [ HYP_EXT:0] itlb_content; - logic [ PT_LEVELS-2:0] itlb_is_page; - logic itlb_lu_hit; - logic [ riscv::GPLEN-1:0] itlb_gpaddr; - logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - - logic dtlb_lu_access; - pte_cva6_t [ HYP_EXT:0] dtlb_content; - logic [ PT_LEVELS-2:0] dtlb_is_page; - logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; - logic dtlb_lu_hit; - logic [ riscv::GPLEN-1:0] dtlb_gpaddr; - - logic shared_tlb_access; - logic shared_tlb_hit, itlb_req; - - // Assignments - - assign itlb_lu_access = icache_areq_i.fetch_req; - assign dtlb_lu_access = lsu_req_i; - - - cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (INSTR_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (enable_translation_i), - .update_i (update_itlb), - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), - .lu_is_page_o (itlb_is_page), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (en_ld_st_translation_i), - .update_i (update_dtlb), - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), - .lu_is_page_o (dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) - ); - - - cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT (HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT (HYP_EXT), - .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), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o(update_vaddr), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i(hlvx_inst_i), - // from CSR file - .satp_ppn_i (satp_ppn_i), - .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_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 - // .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 int 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 - 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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT == 0) && // - (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // - || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); - if (HYP_EXT == 1) - iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 +// 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_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 +// .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 int 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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 + + for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 +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[0] = icache_areq_i.fetch_req && (enable_translation_i[0] || HYP_EXT == 0) && // + (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content[0].u) // + || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content[0].u)); + + if (HYP_EXT == 1) + iaccess_err[HYP_EXT] = icache_areq_i.fetch_req && enable_translation_i[HYP_EXT] && !itlb_content[HYP_EXT].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[HYP_EXT:0])) 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 + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + else + 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; + // ---------// + // 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; + if (HYP_EXT == 1 && iaccess_err[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, + itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + enable_translation_i[HYP_EXT*2], + 1'b1 + }; + // we got an access error + else if (iaccess_err[0]) + // throw a page fault if (HYP_EXT == 1) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, @@ -392,111 +425,57 @@ module cva6_mmu }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, 1'b1 + 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; - if (HYP_EXT == 1 && iaccess_err[HYP_EXT]) + else if (!pmp_instr_allow) + if (HYP_EXT == 1) icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, + riscv::INSTR_ACCESS_FAULT, {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - itlb_gpaddr[riscv::GPLEN-1:0], + {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[HYP_EXT*2], 1'b1 }; - // we got an access error - else if (iaccess_err[0]) - // throw a page fault - if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, - 1'b1 - }; - else if (!pmp_instr_allow) - if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_i.fetch_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 - }; - end else if (ptw_active && walking_instr) begin - // ---------// - // ITLB Miss - // ---------// - // watch out for exceptions happening during walking the page table - icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; - if (ptw_error[0]) - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) - icache_areq_o.fetch_exception = { - riscv::INSTR_GUEST_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else if (HYP_EXT == 1) - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, - {riscv::XLEN'(update_vaddr)}, - {riscv::GPLEN{1'b0}}, - {riscv::XLEN{1'b0}}, - enable_translation_i[2*HYP_EXT], - 1'b1 - }; - else - icache_areq_o.fetch_exception = { - riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, 1'b1 - }; + else + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, riscv::XLEN'(icache_areq_i.fetch_vaddr), 1'b1 + }; + end else if (ptw_active && walking_instr) begin + // ---------// + // ITLB Miss + // ---------// + // watch out for exceptions happening during walking the page table + icache_areq_o.fetch_valid = ptw_error[0] | ptw_access_exception; + if (ptw_error[0]) + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) + icache_areq_o.fetch_exception = { + riscv::INSTR_GUEST_PAGE_FAULT, + {riscv::XLEN'(update_vaddr)}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), + enable_translation_i[2*HYP_EXT], + 1'b1 + }; else if (HYP_EXT == 1) icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(update_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, - enable_translation_i[HYP_EXT*2], + enable_translation_i[2*HYP_EXT], 1'b1 }; else icache_areq_o.fetch_exception = { - riscv::INSTR_ACCESS_FAULT, - ptw_bad_paddr[0][riscv::PLEN-1:(riscv::PLEN>riscv::VLEN)?(riscv::PLEN-riscv::VLEN) : 0], - 1'b1 + riscv::INSTR_PAGE_FAULT, {riscv::XLEN'(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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) - if (HYP_EXT == 1) + else if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, - {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::XLEN'(update_vaddr)}, {riscv::GPLEN{1'b0}}, {riscv::XLEN{1'b0}}, enable_translation_i[2*HYP_EXT], @@ -505,158 +484,287 @@ module cva6_mmu else 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]), + ptw_bad_paddr[0][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} - ); - // assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now - - // Instruction fetch - pmp #( - .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension - ) 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 [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; - logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; - logic hs_ld_st_inst_n, hs_ld_st_inst_q; - pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; - assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: - dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // - (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); - - assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: - dtlb_content[0].ppn[11:0]) : // - lsu_vaddr_n[0][23:12]; - - genvar i; - generate - - for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o - assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // - (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: - dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? - lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// - (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 - assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? - (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: - dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): - lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; - 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[0]|| HYP_EXT==0) ) || (!(|enable_translation_i[HYP_EXT:0]) && !pmp_instr_allow)) + if (HYP_EXT == 1) + icache_areq_o.fetch_exception = { + riscv::INSTR_ACCESS_FAULT, + {riscv::XLEN'(icache_areq_o.fetch_paddr)}, + {riscv::GPLEN{1'b0}}, + {riscv::XLEN{1'b0}}, + enable_translation_i[2*HYP_EXT], + 1'b1 + }; + else + 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 + +// 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} +); +// assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}); this is the package used in the hypervisor extension for now + +// Instruction fetch +pmp #( + .CVA6Cfg (CVA6Cfg), //comment for hypervisor extension + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) configuration used in hypervisor extension +) 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) +); - endgenerate +//----------------------- +// Data Interface +//----------------------- +logic [HYP_EXT:0][riscv::VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q; +logic [riscv::XLEN-1:0] lsu_tinst_n, lsu_tinst_q; +logic hs_ld_st_inst_n, hs_ld_st_inst_q; +pte_cva6_t [HYP_EXT:0] 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[HYP_EXT:0]) ? 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[0][11:0]; +assign lsu_paddr_o [riscv::PLEN-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))]: + dtlb_pte_q[0].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] ): // + (riscv::PLEN-PPNWMin-1)'(lsu_vaddr_q[0][((riscv::PLEN > riscv::VLEN) ? riscv::VLEN : riscv::PLEN )-1:PPNWMin+1]); + +assign lsu_dtlb_ppn_o[11:0] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[11:0]: + dtlb_content[0].ppn[11:0]) : // + lsu_vaddr_n[0][23:12]; + +genvar i; +generate + + for (i = 0; i < PT_LEVELS - 1; i++) begin : gen_paddr_ppn_o + assign lsu_paddr_o [PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1] = // + (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_pte_q[HYP_EXT].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)))]: + dtlb_pte_q[0].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[0][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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]==0)) ? // + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].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[HYP_EXT:0] && !misaligned_ex_q.valid && (|dtlb_is_page_q[i:0]!=0)? + lsu_vaddr_n[0][PPNWMin-((VPN_LEN/PT_LEVELS)*(i)):PPNWMin-((VPN_LEN/PT_LEVELS)*(i+1))+1]:// + (VPN_LEN/PT_LEVELS)'(lsu_vaddr_n[0][((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 : gen_ppn_64 + assign lsu_dtlb_ppn_o[riscv::PPNW-1:PPNWMin+1] = (|en_ld_st_translation_i[HYP_EXT:0] && !misaligned_ex_q.valid) ? + (en_ld_st_translation_i[HYP_EXT] ? dtlb_content[HYP_EXT].ppn[riscv::PPNW-1:PPNWMin+1]: + dtlb_content[0].ppn[riscv::PPNW-1:PPNWMin+1] ): + lsu_vaddr_n[0][riscv::PLEN-1:PPNWMin+1] ; + end - // 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[0] = lsu_vaddr_i; - lsu_tinst_n = lsu_tinst_i; +endgenerate - lsu_req_n = lsu_req_i; - hs_ld_st_inst_n = hs_ld_st_inst_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[0] = lsu_vaddr_i; + lsu_tinst_n = lsu_tinst_i; - if (HYP_EXT == 1) begin - lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; - end + lsu_req_n = lsu_req_i; + hs_ld_st_inst_n = hs_ld_st_inst_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; - csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& - ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); - - if (HYP_EXT == 1) daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; - // translation is enabled and no misaligned exception occurred - if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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) + if (HYP_EXT == 1) begin + lsu_vaddr_n[HYP_EXT] = dtlb_gpaddr; + end + + lsu_valid_o = lsu_req_q; + lsu_exception_o = misaligned_ex_q; + csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_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[0] = (en_ld_st_translation_i[0] || HYP_EXT==0)&& + ((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (en_ld_st_translation_i[HYP_EXT*2] ? !sum_i[HYP_EXT] : !sum_i[0] ) && dtlb_pte_q[0].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[0].u)); + + if (HYP_EXT == 1) daccess_err[HYP_EXT] = en_ld_st_translation_i[HYP_EXT] && !dtlb_pte_q[1].u; + // translation is enabled and no misaligned exception occurred + if ((|en_ld_st_translation_i[HYP_EXT:0]) && !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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + lsu_exception_o = { + riscv::STORE_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::STORE_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {riscv::XLEN'(lsu_paddr_o)}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else 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 - // this is a store + // this is a load + end else begin + if (HYP_EXT == 1 && daccess_err[HYP_EXT]) begin + lsu_exception_o = { + riscv::LOAD_GUEST_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + lsu_vaddr_q[1][riscv::GPLEN-1:0], + {riscv::XLEN{1'b0}}, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + // check for sufficient access privileges - throw a page fault if necessary + end else if (daccess_err[0]) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else begin + lsu_exception_o = { + riscv::LOAD_PAGE_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + 1'b1 + }; + end + // Check if any PMPs are violated + end else if (!pmp_data_allow) begin + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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 else + + // --------- + // DTLB Miss + // --------- + // watch out for exceptions + if (ptw_active && !walking_instr) begin + // page table walker threw an exception + if (ptw_error[0]) 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(HYP_EXT==1 && en_ld_st_translation_i[HYP_EXT] && (!dtlb_pte_q[HYP_EXT].w || daccess_err[HYP_EXT] || !dtlb_pte_q[HYP_EXT].d)) begin + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin lsu_exception_o = { riscv::STORE_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else if ((en_ld_st_translation_i[0] || HYP_EXT==0) && (!dtlb_pte_q[0].w || daccess_err[0] || !dtlb_pte_q[0].d)) begin + end else begin if (HYP_EXT == 1) begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -665,47 +773,26 @@ module cva6_mmu end else begin lsu_exception_o = { riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {riscv::XLEN'(lsu_paddr_o)}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else 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]), + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, 1'b1 }; end end - - // this is a load end else begin - if (HYP_EXT == 1 && daccess_err[HYP_EXT]) begin + if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin lsu_exception_o = { riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - lsu_vaddr_q[1][riscv::GPLEN-1:0], - {riscv::XLEN{1'b0}}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], + (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - // check for sufficient access privileges - throw a page fault if necessary - end else if (daccess_err[0]) begin + end else begin if (HYP_EXT == 1) begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, {riscv::GPLEN{1'b0}}, lsu_tinst_q, en_ld_st_translation_i[HYP_EXT*2], @@ -714,142 +801,18 @@ module cva6_mmu end else begin lsu_exception_o = { riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - 1'b1 - }; - end - // Check if any PMPs are violated - end else if (!pmp_data_allow) begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, lsu_vaddr_q[0]}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 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 else - - // --------- - // DTLB Miss - // --------- - // watch out for exceptions - if (ptw_active && !walking_instr) begin - // page table walker threw an exception - if (ptw_error[0]) 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 - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::STORE_GUEST_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], 1'b1 }; - end else begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::STORE_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end - end - end else begin - if (HYP_EXT == 1 && ptw_error[HYP_EXT]) begin - lsu_exception_o = { - riscv::LOAD_GUEST_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - ptw_bad_paddr[HYP_EXT][riscv::GPLEN-1:0], - (ptw_error[HYP_EXT*2] ? (riscv::IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {riscv::XLEN{1'b0}}), - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LOAD_PAGE_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - 1'b1 - }; - end 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 - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else begin - lsu_exception_o = { - riscv::LD_ACCESS_FAULT, - ptw_bad_paddr[0][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 - if (HYP_EXT == 1) begin - lsu_exception_o = { - riscv::ST_ACCESS_FAULT, - {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, - {riscv::GPLEN{1'b0}}, - lsu_tinst_q, - en_ld_st_translation_i[HYP_EXT*2], - 1'b1 - }; - end else - 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 + + 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 if (HYP_EXT == 1) begin lsu_exception_o = { riscv::LD_ACCESS_FAULT, @@ -862,55 +825,92 @@ module cva6_mmu 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], + ptw_bad_paddr[0][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), // COMMENT IN HYPERVISOR EXTENSION - .PLEN (riscv::PLEN), - .PMP_LEN (riscv::PLEN - 2), - .NR_ENTRIES(CVA6Cfg.NrPMPEntries) - // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION - ) 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_tinst_q <= '0; - hs_ld_st_inst_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 + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::ST_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 1'b1 + }; + end else + 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_tinst_q <= lsu_tinst_n; - hs_ld_st_inst_q <= hs_ld_st_inst_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; + if (HYP_EXT == 1) begin + lsu_exception_o = { + riscv::LD_ACCESS_FAULT, + {{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[0][riscv::VLEN-1]}}, update_vaddr}, + {riscv::GPLEN{1'b0}}, + lsu_tinst_q, + en_ld_st_translation_i[HYP_EXT*2], + 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), // COMMENT IN HYPERVISOR EXTENSION + .PLEN (riscv::PLEN), + .PMP_LEN (riscv::PLEN - 2), + .NR_ENTRIES(CVA6Cfg.NrPMPEntries) + // .NR_ENTRIES ( ArianeCfg.NrPMPEntries ) CONFIGURATION USED IN HYPERVISOR EXTENSION +) 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_tinst_q <= '0; + hs_ld_st_inst_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_tinst_q <= lsu_tinst_n; + hs_ld_st_inst_q <= hs_ld_st_inst_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 f1374161f2b5ad7e37ca7aa33a5cfd0e56000028 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:23:44 +0100 Subject: [PATCH 171/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 8266d99f91..f670336626 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -19,16 +19,16 @@ module cva6_mmu -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now - parameter int unsigned INSTR_TLB_ENTRIES = 4, - parameter int unsigned DATA_TLB_ENTRIES = 4, - parameter logic HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0], - 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 ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig, //This is the required config param in the hypervisor version for now + parameter int unsigned INSTR_TLB_ENTRIES = 4, + parameter int unsigned DATA_TLB_ENTRIES = 4, + parameter logic HYP_EXT = 0, + parameter int unsigned ASID_WIDTH [HYP_EXT:0], + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( input logic clk_i, @@ -98,7 +98,7 @@ generate (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; end -endgenerate + endgenerate // memory management, pte for cva6 localparam type pte_cva6_t = struct packed { From 14b638f0787ad0a2d2d2c0bf8838e4daecc76b08 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:31:37 +0100 Subject: [PATCH 172/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_mmu.sv | 626 ++++++++++++++++++------------------- 1 file changed, 313 insertions(+), 313 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index f670336626..12677451b6 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -31,328 +31,328 @@ module cva6_mmu parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, - input logic rst_ni, - input logic flush_i, - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st - // IF interface - input icache_arsp_t icache_areq_i, - output icache_areq_t icache_areq_o, - // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now - // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in - input logic lsu_is_store_i, // the translation is requested by a store - output logic csr_hs_ld_st_inst_o, // hyp load store instruction - // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, - input logic [HYP_EXT:0] mxr_i, - input logic hlvx_inst_i, - input logic hs_ld_st_inst_i, - // input logic flag_mprv_i, - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - - input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] - 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 [HYP_EXT*2:0] 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 [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for ld/st + // IF interface + input icache_arsp_t icache_areq_i, + output icache_areq_t icache_areq_o, + // input icache_areq_o_t icache_areq_i, this is the data type in the hypervisor version for now + // output icache_areq_i_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 riscv::xlen_t lsu_tinst_i, // transformed instruction in + input logic lsu_is_store_i, // the translation is requested by a store + output logic csr_hs_ld_st_inst_o, // hyp load store instruction + // 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 same cycle as the request if translation hits in 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 [HYP_EXT:0] sum_i, + input logic [HYP_EXT:0] mxr_i, + input logic hlvx_inst_i, + input logic hs_ld_st_inst_i, + // input logic flag_mprv_i, + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + + input logic [ASID_WIDTH[0]-1:0] asid_i [HYP_EXT*2:0], //[vmid,vs_asid,asid] + 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 [HYP_EXT*2:0] 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 [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; -logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] dtlb_mmu_asid_i[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] itlb_mmu_asid_i[HYP_EXT:0]; -genvar b; -generate - for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid - assign dtlb_mmu_asid_i[b] = b==0 ? + genvar b; + generate + for (b = 0; b < HYP_EXT + 1; b++) begin : gen_tlbs_asid + assign dtlb_mmu_asid_i[b] = b==0 ? ((en_ld_st_translation_i[2*HYP_EXT] || flush_tlb_i[HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; - assign itlb_mmu_asid_i[b] = b==0 ? + assign itlb_mmu_asid_i[b] = b==0 ? (enable_translation_i[2*HYP_EXT] ? asid_i[HYP_EXT] : asid_i[0]): asid_i[HYP_EXT*2]; - end + end endgenerate -// memory management, pte for cva6 -localparam type pte_cva6_t = 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 { - logic valid; - logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; - logic [VPN_LEN-1:0] vpn; - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; - logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled - pte_cva6_t [HYP_EXT:0] content; -}; - -logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page -logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception -logic ptw_access_exception; // PTW threw an access exception (PMPs) -logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr - -logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; - -tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; - -logic itlb_lu_access; -pte_cva6_t [ HYP_EXT:0] itlb_content; -logic [ PT_LEVELS-2:0] itlb_is_page; -logic itlb_lu_hit; -logic [ riscv::GPLEN-1:0] itlb_gpaddr; -logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; - -logic dtlb_lu_access; -pte_cva6_t [ HYP_EXT:0] dtlb_content; -logic [ PT_LEVELS-2:0] dtlb_is_page; -logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; -logic dtlb_lu_hit; -logic [ riscv::GPLEN-1:0] dtlb_gpaddr; - -logic shared_tlb_access; -logic shared_tlb_hit, itlb_req; - -// Assignments - -assign itlb_lu_access = icache_areq_i.fetch_req; -assign dtlb_lu_access = lsu_req_i; - - -cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (INSTR_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (enable_translation_i), - .update_i (update_itlb), - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), - .lu_is_page_o (itlb_is_page), - .lu_hit_o (itlb_lu_hit) -); - -cva6_tlb #( - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .TLB_ENTRIES (DATA_TLB_ENTRIES), - .HYP_EXT (HYP_EXT), - .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), - .v_st_enbl_i (en_ld_st_translation_i), - .update_i (update_dtlb), - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), - .lu_is_page_o (dtlb_is_page), - .lu_hit_o (dtlb_lu_hit) -); - - -cva6_shared_tlb #( - .SHARED_TLB_DEPTH (64), - .SHARED_TLB_WAYS (2), - .HYP_EXT (HYP_EXT), - .ASID_WIDTH (ASID_WIDTH), - .VPN_LEN (VPN_LEN), - .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), - .flush_i(flush_tlb_i), - .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), - - .dtlb_asid_i (dtlb_mmu_asid_i), - .itlb_asid_i (itlb_mmu_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), - // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now - .pte_cva6_t (pte_cva6_t), - .tlb_update_cva6_t(tlb_update_cva6_t), - .HYP_EXT (HYP_EXT), - .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), - - .enable_translation_i (enable_translation_i), - .en_ld_st_translation_i(en_ld_st_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), - // .enable_translation_i ( enable_translation_i ), - // .en_ld_st_translation_i ( en_ld_st_translation_i), - .asid_i (asid_i), - - .update_vaddr_o(update_vaddr), - - // to Shared TLB, update logic - .shared_tlb_update_o(update_shared_tlb), - - - // 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), - // .dtlb_access_i ( dtlb_lu_access ), - // .dtlb_hit_i ( dtlb_lu_hit ), - // .dtlb_vaddr_i ( lsu_vaddr_i ), - .hlvx_inst_i(hlvx_inst_i), - // from CSR file - .satp_ppn_i (satp_ppn_i), - .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_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 -// .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 int 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[HYP_EXT:0]) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - PPNWMin-1))] : + // memory management, pte for cva6 + localparam type pte_cva6_t = 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 { + logic valid; + logic [PT_LEVELS-2:0][HYP_EXT:0] is_page; + logic [VPN_LEN-1:0] vpn; + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] asid; + logic [HYP_EXT*2:0] v_st_enbl; // v_i,g-stage enabled, s-stage enabled + pte_cva6_t [HYP_EXT:0] content; + }; + + logic [HYP_EXT:0] iaccess_err; // insufficient privilege to access this instruction page + logic [HYP_EXT:0] 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 [HYP_EXT*2:0] ptw_error; // PTW threw an exception + logic ptw_access_exception; // PTW threw an access exception (PMPs) + logic [HYP_EXT:0][riscv::PLEN-1:0] ptw_bad_paddr; // PTW guest page fault bad guest physical addr + + logic [riscv::VLEN-1:0] update_vaddr, shared_tlb_vaddr; + + tlb_update_cva6_t update_itlb, update_dtlb, update_shared_tlb; + + logic itlb_lu_access; + pte_cva6_t [ HYP_EXT:0] itlb_content; + logic [ PT_LEVELS-2:0] itlb_is_page; + logic itlb_lu_hit; + logic [ riscv::GPLEN-1:0] itlb_gpaddr; + logic [ASID_WIDTH[0]-1:0] itlb_lu_asid; + + logic dtlb_lu_access; + pte_cva6_t [ HYP_EXT:0] dtlb_content; + logic [ PT_LEVELS-2:0] dtlb_is_page; + logic [ASID_WIDTH[0]-1:0] dtlb_lu_asid; + logic dtlb_lu_hit; + logic [ riscv::GPLEN-1:0] dtlb_gpaddr; + + logic shared_tlb_access; + logic shared_tlb_hit, itlb_req; + + // Assignments + + assign itlb_lu_access = icache_areq_i.fetch_req; + assign dtlb_lu_access = lsu_req_i; + + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (INSTR_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (enable_translation_i), + .update_i (update_itlb), + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_mmu_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_gpaddr_o (itlb_gpaddr), + .lu_is_page_o (itlb_is_page), + .lu_hit_o (itlb_lu_hit) + ); + + cva6_tlb #( + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .TLB_ENTRIES (DATA_TLB_ENTRIES), + .HYP_EXT (HYP_EXT), + .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), + .v_st_enbl_i (en_ld_st_translation_i), + .update_i (update_dtlb), + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_mmu_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_gpaddr_o (dtlb_gpaddr), + .lu_is_page_o (dtlb_is_page), + .lu_hit_o (dtlb_lu_hit) + ); + + + cva6_shared_tlb #( + .SHARED_TLB_DEPTH (64), + .SHARED_TLB_WAYS (2), + .HYP_EXT (HYP_EXT), + .ASID_WIDTH (ASID_WIDTH), + .VPN_LEN (VPN_LEN), + .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), + .flush_i(flush_tlb_i), + .v_st_enbl_i({enable_translation_i, en_ld_st_translation_i}), + + .dtlb_asid_i (dtlb_mmu_asid_i), + .itlb_asid_i (itlb_mmu_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), + // .ArianeCfg ( ArianeCfg ), this is the configuration needed in the hypervisor extension for now + .pte_cva6_t (pte_cva6_t), + .tlb_update_cva6_t(tlb_update_cva6_t), + .HYP_EXT (HYP_EXT), + .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), + + .enable_translation_i (enable_translation_i), + .en_ld_st_translation_i(en_ld_st_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), + // .enable_translation_i ( enable_translation_i ), + // .en_ld_st_translation_i ( en_ld_st_translation_i), + .asid_i (asid_i), + + .update_vaddr_o(update_vaddr), + + // to Shared TLB, update logic + .shared_tlb_update_o(update_shared_tlb), + + + // 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), + // .dtlb_access_i ( dtlb_lu_access ), + // .dtlb_hit_i ( dtlb_lu_hit ), + // .dtlb_vaddr_i ( lsu_vaddr_i ), + .hlvx_inst_i(hlvx_inst_i), + // from CSR file + .satp_ppn_i (satp_ppn_i), + .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_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 + // .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 int 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[HYP_EXT:0]) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].ppn[riscv::PPNW-1:(riscv::PPNW - (riscv::PLEN - 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 - - for (a = 0; a < PT_LEVELS - 1; a++) begin : gen_fetch_paddr - assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // - (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // - (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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)))]: + (riscv::PLEN-PPNWMin-1)'(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 : gen_fetch_paddr + assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // + (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // + (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 From 41b0d7deeb99802baf6f232797122452ea3ccb30 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:35:40 +0100 Subject: [PATCH 173/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 12677451b6..6abc57e07a 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -353,8 +353,8 @@ module cva6_mmu assign icache_areq_o.fetch_paddr [PPNWMin-((VPN_LEN/PT_LEVELS)*(a)):PPNWMin-((VPN_LEN/PT_LEVELS)*(a+1))+1] = // (|enable_translation_i[HYP_EXT:0] && (|itlb_is_page[a:0] == 0)) ? // (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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]; + 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 endgenerate From 996be086ae323eac92460f105e9beb7843b80d63 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:40:03 +0100 Subject: [PATCH 174/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index 6abc57e07a..d1482c624d 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -355,8 +355,8 @@ module cva6_mmu (enable_translation_i[HYP_EXT] ? itlb_content[HYP_EXT].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 -endgenerate + end + endgenerate // The instruction interface is a simple request response interface always_comb begin : instr_interface From 7aacc52523804333f206657f06121aa57540465f Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:41:50 +0100 Subject: [PATCH 175/182] linting --- core/mmu_unify/cva6_mmu.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_mmu.sv b/core/mmu_unify/cva6_mmu.sv index d1482c624d..ccd30badb8 100644 --- a/core/mmu_unify/cva6_mmu.sv +++ b/core/mmu_unify/cva6_mmu.sv @@ -380,7 +380,7 @@ always_comb begin : instr_interface // an error. if ((|enable_translation_i[HYP_EXT:0])) 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 + 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)) if (HYP_EXT == 1) icache_areq_o.fetch_exception = { riscv::INSTR_ACCESS_FAULT, @@ -394,7 +394,7 @@ always_comb begin : instr_interface 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; // ---------// // ITLB Hit From a55fd1d8e6bf2d13ebf1becba38fd769ef6cd55c Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 17:59:34 +0100 Subject: [PATCH 176/182] linting ptw --- core/mmu_unify/cva6_ptw.sv | 1071 ++++++++++++++++++------------------ 1 file changed, 547 insertions(+), 524 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index 553c23c0c2..f72dbe82b7 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -16,566 +16,589 @@ /* verilator lint_off WIDTH */ -module cva6_ptw import ariane_pkg::*; #( - parameter type pte_cva6_t = logic, - parameter type tlb_update_cva6_t = logic, - parameter int unsigned HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, - parameter int unsigned VPN_LEN = 1, - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned PT_LEVELS = 1 +module cva6_ptw +import ariane_pkg::*; +#( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH[HYP_EXT:0] = {1}, + parameter int unsigned VPN_LEN = 1, + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + 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 [HYP_EXT*2:0] ptw_error_o, // set when an error occurred - output logic ptw_access_exception_o, // set when an PMP access exception occured - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - input logic hlvx_inst_i, // is a HLVX load/store instruction + 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 [HYP_EXT*2:0] ptw_error_o, // set when an error occurred + output logic ptw_access_exception_o, // set when an PMP access exception occured + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + input logic hlvx_inst_i, // is a HLVX load/store instruction - 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, + 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 TLBs, update logic - output tlb_update_cva6_t shared_tlb_update_o, + // to TLBs, update logic + output tlb_update_cva6_t shared_tlb_update_o, - output logic [riscv::VLEN-1:0] update_vaddr_o, + output logic [riscv::VLEN-1:0] update_vaddr_o, - input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0],//[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0], //[vmid,vs_asid,asid] - // from TLBs - // did we miss? - input logic shared_tlb_access_i, - input logic shared_tlb_hit_i, - input logic [riscv::VLEN-1:0] shared_tlb_vaddr_i, + // from TLBs + // did we miss? + 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, + input logic itlb_req_i, - // from CSR file - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0],//[hgatp,vsatp,satp] - input logic [HYP_EXT:0] mxr_i, + // from CSR file + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + input logic [ HYP_EXT:0] mxr_i, - // Performance counters - output logic shared_tlb_miss_o, + // Performance counters + output logic shared_tlb_miss_o, - // PMP + // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, - output logic [HYP_EXT:0][riscv::PLEN-1:0] bad_paddr_o + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, + output logic [HYP_EXT:0][riscv::PLEN-1:0] bad_paddr_o ); - // input registers - logic data_rvalid_q; - riscv::xlen_t data_rdata_q; - - pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] - // register to perform context switch between stages - // pte_cva6_t gpte_q, gpte_d; - assign pte[0] = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); - - enum logic[2:0] { - IDLE, - WAIT_GRANT, - PTE_LOOKUP, - WAIT_RVALID, - PROPAGATE_ERROR, - PROPAGATE_ACCESS_ERROR, - LATENCY - } state_q, state_d; - - logic [PT_LEVELS-1:0] misaligned_page; - logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_n,ptw_lvl_q; - - // define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE - // S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs - // G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp) - // G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp) - enum logic [1:0] { - S_STAGE, - G_INTERMED_STAGE, - G_FINAL_STAGE - } ptw_stage_q, ptw_stage_d; - - // 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 ASIDs - logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; - // register the VPN we need to walk, SV39 defines a 39 bit virtual address - logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; - logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; - // register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage - logic [riscv::GPLEN-1:0] gpaddr_q, gpaddr_n,gpaddr_base; - logic [PT_LEVELS-2:0][riscv::GPLEN-1:0] gpaddr; - // 4 byte aligned physical pointer - logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; - logic [riscv::PLEN-1:0] gptw_pptr_q, gptw_pptr_n; - - // Assignments - assign update_vaddr_o = vaddr_q; - - assign ptw_active_o = (state_q != IDLE); - 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; - - // ----------- - // TLB Update - // ----------- - - assign gpaddr_base = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; - - genvar z,w; - generate - for (z=0; z < PT_LEVELS-1; z++) begin - - // 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[z] = (ptw_lvl_q[0] == (z)) && (pte[0].ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-z)-1:0] != '0); - - //record the vaddr corresponding to each level - for (w=0; w < HYP_EXT*2+1; w++) begin - assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))] : - w==1 ? gptw_pptr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]: - gpaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]; - end +// input registers +logic data_rvalid_q; +riscv::xlen_t data_rdata_q; + +pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] +// register to perform context switch between stages +// pte_cva6_t gpte_q, gpte_d; +assign pte[0] = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); + +enum logic [2:0] { + IDLE, + WAIT_GRANT, + PTE_LOOKUP, + WAIT_RVALID, + PROPAGATE_ERROR, + PROPAGATE_ACCESS_ERROR, + LATENCY +} + state_q, state_d; + +logic [PT_LEVELS-1:0] misaligned_page; +logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_n, ptw_lvl_q; + +// define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE +// S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs +// G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp) +// G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp) +enum logic [1:0] { + S_STAGE, + G_INTERMED_STAGE, + G_FINAL_STAGE +} + ptw_stage_q, ptw_stage_d; + +// 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 ASIDs +logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; +// register the VPN we need to walk, SV39 defines a 39 bit virtual address +logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; +logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; +// register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage +logic [riscv::GPLEN-1:0] gpaddr_q, gpaddr_n, gpaddr_base; +logic [PT_LEVELS-2:0][riscv::GPLEN-1:0] gpaddr; +// 4 byte aligned physical pointer +logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; +logic [riscv::PLEN-1:0] gptw_pptr_q, gptw_pptr_n; + +// Assignments +assign update_vaddr_o = vaddr_q; + +assign ptw_active_o = (state_q != IDLE); +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; + +// ----------- +// TLB Update +// ----------- + +assign gpaddr_base = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; + +genvar z, w; +generate + for (z = 0; z < PT_LEVELS - 1; z++) begin + + // 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[z] = (ptw_lvl_q[0] == (z)) && (pte[0].ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-z)-1:0] != '0); + + //record the vaddr corresponding to each level + for (w = 0; w < HYP_EXT * 2 + 1; w++) begin + assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))] : + w==1 ? gptw_pptr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]: + gpaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]; + end + + assign gpaddr[z][VPN_LEN-(VPN_LEN/PT_LEVELS):0]= (ptw_lvl_q[0] == z) ? vaddr_q[VPN_LEN-(VPN_LEN/PT_LEVELS):0] : gpaddr_base[VPN_LEN-(VPN_LEN/PT_LEVELS):0]; + assign gpaddr[z][VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1] : gpaddr_base[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]; + assign gpaddr[z][riscv::GPLEN-1:VPN_LEN+1] = gpaddr_base[riscv::GPLEN-1:VPN_LEN+1]; + + + end +endgenerate + +always_comb begin : tlb_update + + shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + + // update the correct page table level + for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin + for (int unsigned x = 0; x < PT_LEVELS - 1; x++) begin + if((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0])&& HYP_EXT==1) begin + shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT?0 : 1] == x); + end else if (enable_translation_i[0] || en_ld_st_translation_i[0] || HYP_EXT == 0) begin + shared_tlb_update_o.is_page[x][y] = y == 0 ? (ptw_lvl_q[0] == x) : 1'b0; + end else begin + shared_tlb_update_o.is_page[x][y] = y != 0 ? (ptw_lvl_q[0] == x) : 1'b0; + end + end + + // set the global mapping bit + if ((enable_translation_i[HYP_EXT] || en_ld_st_translation_i[HYP_EXT]) && HYP_EXT == 1) begin + shared_tlb_update_o.content[y] = y == 0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; + end else begin + shared_tlb_update_o.content[y] = y == 0 ? (pte[0] | (global_mapping_q << 5)) : '0; + end + end + // output the correct ASIDs + shared_tlb_update_o.asid = tlb_update_asid_q; + + bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; + if (HYP_EXT == 1) + bad_paddr_o[HYP_EXT][riscv::GPLEN:0] = ptw_error_o[HYP_EXT] ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[riscv::GPLEN:0] : gpaddr_q) : 'b0; +end + +assign req_port_o.tag_valid = tag_valid_q; + +logic allow_access; + + + +pmp #( + .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 gpaddr[z][VPN_LEN-(VPN_LEN/PT_LEVELS):0]= (ptw_lvl_q[0] == z) ? vaddr_q[VPN_LEN-(VPN_LEN/PT_LEVELS):0] : gpaddr_base[VPN_LEN-(VPN_LEN/PT_LEVELS):0]; - assign gpaddr[z][VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1] : gpaddr_base[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]; - assign gpaddr[z][riscv::GPLEN-1:VPN_LEN+1]= gpaddr_base[riscv::GPLEN-1:VPN_LEN+1]; +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 +//------------------- +// 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, or if any bits or encodings +// that are reserved for future standard use are set within pte, stop and raise +// a page-fault exception corresponding to the original access type. +// 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 + automatic logic [riscv::PLEN-1:0] pptr; + // automatic logic [riscv::GPLEN-1:0] gpaddr; + // default assignments + // 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_we = 1'b0; + ptw_error_o = '0; + 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; + gptw_pptr_n = gptw_pptr_q; + state_d = state_q; + ptw_stage_d = ptw_stage_q; + global_mapping_n = global_mapping_q; + // input registers + tlb_update_asid_n = tlb_update_asid_q; + vaddr_n = vaddr_q; + gpaddr_n = gpaddr_q; + pptr = ptw_pptr_q; + // gpaddr = gpaddr_q; + + shared_tlb_miss_o = 1'b0; + + if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[HYP_EXT]; + + case (state_q) + + IDLE: begin + // by default we start with the top-most page table + ptw_lvl_n = '0; + global_mapping_n = 1'b0; + is_instr_ptw_n = 1'b0; + gpaddr_n = '0; + + if (HYP_EXT == 1) pte[HYP_EXT*2] = '0; + // if we got an ITLB miss + if (((|enable_translation_i[HYP_EXT:0]) || |en_ld_st_translation_i[HYP_EXT:0]) && shared_tlb_access_i && ~shared_tlb_hit_i) begin + if ((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0]) && HYP_EXT==1) begin + ptw_stage_d = G_INTERMED_STAGE; + pptr = { + satp_ppn_i[HYP_EXT], + shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + gptw_pptr_n = pptr; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + end else if (((|enable_translation_i[HYP_EXT:0] && !enable_translation_i[0]) || (|en_ld_st_translation_i[HYP_EXT:0] && !en_ld_st_translation_i[0])) && HYP_EXT==1) begin + ptw_stage_d = G_FINAL_STAGE; + gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + end else begin + ptw_stage_d = S_STAGE; + if((enable_translation_i[HYP_EXT*2] || en_ld_st_translation_i[HYP_EXT*2]) && HYP_EXT==1) + ptw_pptr_n = { + satp_ppn_i[HYP_EXT], + shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + else + ptw_pptr_n = { + satp_ppn_i[0], + shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; end - endgenerate - - always_comb begin : tlb_update - - shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); - - // update the correct page table level - for (int unsigned y=0; y < HYP_EXT+1; y++) begin - for (int unsigned x=0; x < PT_LEVELS-1; x++) begin - if((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0])&& HYP_EXT==1) begin - shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT? 0 : 1] == x); - end else if(enable_translation_i[0] || en_ld_st_translation_i[0] || HYP_EXT==0) begin - shared_tlb_update_o.is_page[x][y] = y==0 ? (ptw_lvl_q[0]== x) : 1'b0; - end else begin - shared_tlb_update_o.is_page[x][y] = y!=0 ? (ptw_lvl_q[0]== x) : 1'b0; - end - end - // set the global mapping bit - if((enable_translation_i[HYP_EXT] || en_ld_st_translation_i[HYP_EXT]) && HYP_EXT==1) begin - shared_tlb_update_o.content[y] = y==0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; - end else begin - shared_tlb_update_o.content[y] = y==0 ? (pte[0] | (global_mapping_q << 5)) : '0; - end + is_instr_ptw_n = itlb_req_i; + vaddr_n = shared_tlb_vaddr_i; + state_d = WAIT_GRANT; + shared_tlb_miss_o = 1'b1; + + for (int unsigned b = 0; b < HYP_EXT + 1; b++) begin + tlb_update_asid_n[b] = b==0 ? ((enable_translation_i[2*HYP_EXT] || en_ld_st_translation_i[2*HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; end - // output the correct ASIDs - shared_tlb_update_o.asid = tlb_update_asid_q; - - bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; - if(HYP_EXT==1) - bad_paddr_o[HYP_EXT][riscv::GPLEN:0] = ptw_error_o[HYP_EXT] ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[riscv::GPLEN:0] : gpaddr_q) : 'b0; + end end - assign req_port_o.tag_valid = tag_valid_q; - - logic allow_access; - - - - pmp #( - .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 = 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 - //------------------- - // 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, or if any bits or encodings - // that are reserved for future standard use are set within pte, stop and raise - // a page-fault exception corresponding to the original access type. - // 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 - automatic logic [riscv::PLEN-1:0] pptr; - // automatic logic [riscv::GPLEN-1:0] gpaddr; - // default assignments - // 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_we = 1'b0; - ptw_error_o = '0; - 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; - gptw_pptr_n = gptw_pptr_q; - state_d = state_q; - ptw_stage_d = ptw_stage_q; - global_mapping_n = global_mapping_q; - // input registers - tlb_update_asid_n = tlb_update_asid_q; - vaddr_n = vaddr_q; - gpaddr_n = gpaddr_q; - pptr = ptw_pptr_q; - // gpaddr = gpaddr_q; - - shared_tlb_miss_o = 1'b0; - - if(HYP_EXT==1) - pte[HYP_EXT*2] = pte[HYP_EXT]; - - case (state_q) - - IDLE: begin - // by default we start with the top-most page table - ptw_lvl_n = '0; - global_mapping_n = 1'b0; - is_instr_ptw_n = 1'b0; - gpaddr_n = '0; - - if(HYP_EXT==1) - pte[HYP_EXT*2] = '0; - // if we got an ITLB miss - if (((|enable_translation_i[HYP_EXT:0]) || |en_ld_st_translation_i[HYP_EXT:0]) && shared_tlb_access_i && ~shared_tlb_hit_i) begin - if ((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0]) && HYP_EXT==1) begin - ptw_stage_d = G_INTERMED_STAGE; - pptr = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else if (((|enable_translation_i[HYP_EXT:0] && !enable_translation_i[0]) || (|en_ld_st_translation_i[HYP_EXT:0] && !en_ld_st_translation_i[0])) && HYP_EXT==1) begin - ptw_stage_d = G_FINAL_STAGE; - gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end else begin - ptw_stage_d = S_STAGE; - if((enable_translation_i[HYP_EXT*2] || en_ld_st_translation_i[HYP_EXT*2]) && HYP_EXT==1) - ptw_pptr_n = {satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - else - ptw_pptr_n = {satp_ppn_i[0], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - end - - is_instr_ptw_n = itlb_req_i; - vaddr_n = shared_tlb_vaddr_i; - state_d = WAIT_GRANT; - shared_tlb_miss_o = 1'b1; - - for (int unsigned b=0; b < HYP_EXT+1; b++) begin - tlb_update_asid_n[b] = b==0 ? ((enable_translation_i[2*HYP_EXT] || en_ld_st_translation_i[2*HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; - 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[0].g && ptw_stage_q == S_STAGE) 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[0].v || (!pte[0].r && pte[0].w)) // || (|pte.reserved)) + state_d = PROPAGATE_ERROR; + // ----------- + // Valid PTE + // ----------- + else begin + state_d = LATENCY; + // it is a valid PTE + // if pte.r = 1 or pte.x = 1 it is a valid PTE + if (pte[0].r || pte[0].x) begin + case (ptw_stage_q) + S_STAGE: begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + state_d = WAIT_GRANT; + ptw_stage_d = G_FINAL_STAGE; + if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[0]; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; + gpaddr_n = gpaddr[ptw_lvl_q[0]]; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + ptw_lvl_n[0] = 0; end + end + G_INTERMED_STAGE: begin + state_d = WAIT_GRANT; + ptw_stage_d = S_STAGE; + ptw_lvl_n[0] = ptw_lvl_q[HYP_EXT]; + pptr = {pte[0].ppn[riscv::GPPNW-1:0], gptw_pptr_q[11:0]}; + if (ptw_lvl_q[0] == 1) pptr[20:0] = gptw_pptr_q[20:0]; + if (ptw_lvl_q[0] == 0) pptr[29:0] = gptw_pptr_q[29:0]; + ptw_pptr_n = pptr; + end + default: ; + endcase + // Valid translation found (either 1G, 2M 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[0].x || !pte[0].a) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) + 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin + if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) + shared_tlb_update_o.valid = 1'b1; + end else begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + 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[0].w || !pte[0].d)) begin + shared_tlb_update_o.valid = 1'b0; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + 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 + //if there is a misaligned page, propagate error + if (|misaligned_page) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + shared_tlb_update_o.valid = 1'b0; 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[0].g && ptw_stage_q == S_STAGE) - 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[0].v || (!pte[0].r && pte[0].w))// || (|pte.reserved)) - state_d = PROPAGATE_ERROR; - // ----------- - // Valid PTE - // ----------- - else begin - state_d = LATENCY; - // it is a valid PTE - // if pte.r = 1 or pte.x = 1 it is a valid PTE - if (pte[0].r || pte[0].x) begin - case (ptw_stage_q) - S_STAGE: begin - if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin - state_d = WAIT_GRANT; - ptw_stage_d = G_FINAL_STAGE; - if(HYP_EXT==1) - pte[HYP_EXT*2] = pte[0]; - ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; - gpaddr_n = gpaddr[ptw_lvl_q[0]]; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)],(PT_LEVELS)'(0)}; - ptw_lvl_n[0] = 0; - end - end - G_INTERMED_STAGE: begin - state_d = WAIT_GRANT; - ptw_stage_d = S_STAGE; - ptw_lvl_n[0] = ptw_lvl_q[HYP_EXT]; - pptr = {pte[0].ppn[riscv::GPPNW-1:0], gptw_pptr_q[11:0]}; - if (ptw_lvl_q[0] == 1) - pptr[20:0] = gptw_pptr_q[20:0]; - if(ptw_lvl_q[0] == 0) - pptr[29:0] = gptw_pptr_q[29:0]; - ptw_pptr_n = pptr; - end - default:; - endcase - // Valid translation found (either 1G, 2M 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[0].x || !pte[0].a) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) - 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin - if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) - shared_tlb_update_o.valid = 1'b1; - end else begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - 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[0].w || !pte[0].d)) begin - shared_tlb_update_o.valid = 1'b0; - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - end - end - - //if there is a misaligned page, propagate error - if (|misaligned_page) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - shared_tlb_update_o.valid = 1'b0; - end - - // check if 63:41 are all zeros - if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = G_FINAL_STAGE; - end - // this is a pointer to the next TLB level - end else begin - // pointer to next level of page table - - if (ptw_lvl_q[0] == PT_LEVELS-1) begin - // Should already be the last level page table => Error - ptw_lvl_n[0] = PT_LEVELS-1; - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - - - end else begin - ptw_lvl_n[0] = ptw_lvl_q[0]+1; - state_d = WAIT_GRANT; - - case (ptw_stage_q) - S_STAGE: begin - if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin - ptw_stage_d = G_INTERMED_STAGE; - if(HYP_EXT==1) - pte[HYP_EXT*2] = pte[0]; - ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]+1; - pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - gptw_pptr_n = pptr; - ptw_pptr_n = {satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0)}; - ptw_lvl_n[0] = 0; - end else begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - end - end - G_INTERMED_STAGE: begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - end - G_FINAL_STAGE: begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - end - endcase - - if(HYP_EXT==1 && (pte[0].a || pte[0].d || pte[0].u)) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - end - - end - - // check if 63:41 are all zeros - if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - 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; - ptw_stage_d = ptw_stage_q; - state_d = PROPAGATE_ACCESS_ERROR; - end - end - // we've got a data WAIT_GRANT so tell the cache that the tag is valid + // check if 63:41 are all zeros + if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = G_FINAL_STAGE; end - // Propagate error to MMU/LSU - PROPAGATE_ERROR: begin - state_d = LATENCY; - ptw_error_o[0] = 1'b1; - if(HYP_EXT==1) begin - ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; - ptw_error_o[HYP_EXT*2] = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0; + // this is a pointer to the next TLB level + end else begin + // pointer to next level of page table + + if (ptw_lvl_q[0] == PT_LEVELS - 1) begin + // Should already be the last level page table => Error + ptw_lvl_n[0] = PT_LEVELS - 1; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + + + end else begin + ptw_lvl_n[0] = ptw_lvl_q[0] + 1; + state_d = WAIT_GRANT; + + case (ptw_stage_q) + S_STAGE: begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + ptw_stage_d = G_INTERMED_STAGE; + if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[0]; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1; + pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + gptw_pptr_n = pptr; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + ptw_lvl_n[0] = 0; + end else begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end end + G_INTERMED_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + G_FINAL_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + endcase + + if (HYP_EXT == 1 && (pte[0].a || pte[0].d || pte[0].u)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + 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; + + // check if 63:41 are all zeros + if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; 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 - end - // sequential process - always_ff @(posedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - state_q <= IDLE; - ptw_stage_q <= S_STAGE; - is_instr_ptw_q <= 1'b0; - ptw_lvl_q <= '0; - tag_valid_q <= 1'b0; - tlb_update_asid_q <= '0; - vaddr_q <= '0; - gpaddr_q <= '0; - ptw_pptr_q <= '0; - gptw_pptr_q <= '0; - global_mapping_q <= 1'b0; - data_rdata_q <= '0; - data_rvalid_q <= 1'b0; - if(HYP_EXT==1) - pte[HYP_EXT] <= '0; - end else begin - state_q <= state_d; - ptw_stage_q <= ptw_stage_d; - ptw_pptr_q <= ptw_pptr_n; - gptw_pptr_q <= gptw_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; - gpaddr_q <= gpaddr_n; - global_mapping_q <= global_mapping_n; - data_rdata_q <= req_port_i.data_rdata; - data_rvalid_q <= req_port_i.data_rvalid; - - if(HYP_EXT==1) - pte[HYP_EXT] <= pte[HYP_EXT*2]; + // 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; + ptw_stage_d = ptw_stage_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[0] = 1'b1; + if (HYP_EXT == 1) begin + ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; + ptw_error_o[HYP_EXT*2] = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0; + end + 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; + ptw_stage_q <= S_STAGE; + is_instr_ptw_q <= 1'b0; + ptw_lvl_q <= '0; + tag_valid_q <= 1'b0; + tlb_update_asid_q <= '0; + vaddr_q <= '0; + gpaddr_q <= '0; + ptw_pptr_q <= '0; + gptw_pptr_q <= '0; + global_mapping_q <= 1'b0; + data_rdata_q <= '0; + data_rvalid_q <= 1'b0; + if (HYP_EXT == 1) pte[HYP_EXT] <= '0; + end else begin + state_q <= state_d; + ptw_stage_q <= ptw_stage_d; + ptw_pptr_q <= ptw_pptr_n; + gptw_pptr_q <= gptw_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; + gpaddr_q <= gpaddr_n; + global_mapping_q <= global_mapping_n; + data_rdata_q <= req_port_i.data_rdata; + data_rvalid_q <= req_port_i.data_rvalid; + + if (HYP_EXT == 1) pte[HYP_EXT] <= pte[HYP_EXT*2]; + end +end endmodule /* verilator lint_on WIDTH */ From 58438d84184c72fc01afeb8a871207aaf3f440ab Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 18:02:26 +0100 Subject: [PATCH 177/182] linting --- core/mmu_unify/cva6_ptw.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index f72dbe82b7..c5355cd1ce 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -174,8 +174,6 @@ endgenerate always_comb begin : tlb_update - shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); - // update the correct page table level for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin for (int unsigned x = 0; x < PT_LEVELS - 1; x++) begin @@ -232,6 +230,8 @@ assign req_port_o.data_be = riscv::XLEN == 32 ? be_gen_32( req_port_o.address_index[2:0], req_port_o.data_size ); +assign shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + //------------------- // Page table walker //------------------- From 8dcc1294178dd3cdbe48aa7ab44da4cc2f3fc84e Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino <135128652+AngelaGonzalezMarino@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:05:48 +0100 Subject: [PATCH 178/182] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/mmu_unify/cva6_ptw.sv | 270 ++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index c5355cd1ce..7eafa0d338 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -17,162 +17,162 @@ /* verilator lint_off WIDTH */ module cva6_ptw -import ariane_pkg::*; + import ariane_pkg::*; #( - parameter type pte_cva6_t = logic, - parameter type tlb_update_cva6_t = logic, - parameter int unsigned HYP_EXT = 0, - parameter int unsigned ASID_WIDTH[HYP_EXT:0] = {1}, - parameter int unsigned VPN_LEN = 1, - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned PT_LEVELS = 1 + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH[HYP_EXT:0] = {1}, + parameter int unsigned VPN_LEN = 1, + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + 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 [HYP_EXT*2:0] ptw_error_o, // set when an error occurred - output logic ptw_access_exception_o, // set when an PMP access exception occured - input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] - input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores - input logic hlvx_inst_i, // is a HLVX load/store instruction - - 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, + 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 [HYP_EXT*2:0] ptw_error_o, // set when an error occurred + output logic ptw_access_exception_o, // set when an PMP access exception occured + input logic [HYP_EXT*2:0] enable_translation_i, //[v_i,enable_g_translation,enable_translation] + input logic [HYP_EXT*2:0] en_ld_st_translation_i, // enable virtual memory translation for load/stores + input logic hlvx_inst_i, // is a HLVX load/store instruction + + 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 TLBs, update logic - output tlb_update_cva6_t shared_tlb_update_o, + // to TLBs, update logic + output tlb_update_cva6_t shared_tlb_update_o, - output logic [riscv::VLEN-1:0] update_vaddr_o, + output logic [riscv::VLEN-1:0] update_vaddr_o, - input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0], //[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] asid_i[HYP_EXT*2:0], //[vmid,vs_asid,asid] - // from TLBs - // did we miss? - input logic shared_tlb_access_i, - input logic shared_tlb_hit_i, - input logic [riscv::VLEN-1:0] shared_tlb_vaddr_i, + // from TLBs + // did we miss? + 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, + input logic itlb_req_i, - // from CSR file - input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] - input logic [ HYP_EXT:0] mxr_i, + // from CSR file + input logic [riscv::PPNW-1:0] satp_ppn_i[HYP_EXT*2:0], //[hgatp,vsatp,satp] + input logic [ HYP_EXT:0] mxr_i, - // Performance counters - output logic shared_tlb_miss_o, + // Performance counters + output logic shared_tlb_miss_o, - // PMP + // PMP - input riscv::pmpcfg_t [15:0] pmpcfg_i, - input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, - output logic [HYP_EXT:0][riscv::PLEN-1:0] bad_paddr_o + input riscv::pmpcfg_t [15:0] pmpcfg_i, + input logic [15:0][riscv::PLEN-3:0] pmpaddr_i, + output logic [HYP_EXT:0][riscv::PLEN-1:0] bad_paddr_o ); -// input registers -logic data_rvalid_q; -riscv::xlen_t data_rdata_q; - -pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] -// register to perform context switch between stages -// pte_cva6_t gpte_q, gpte_d; -assign pte[0] = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); - -enum logic [2:0] { - IDLE, - WAIT_GRANT, - PTE_LOOKUP, - WAIT_RVALID, - PROPAGATE_ERROR, - PROPAGATE_ACCESS_ERROR, - LATENCY -} - state_q, state_d; - -logic [PT_LEVELS-1:0] misaligned_page; -logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_n, ptw_lvl_q; - -// define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE -// S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs -// G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp) -// G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp) -enum logic [1:0] { - S_STAGE, - G_INTERMED_STAGE, - G_FINAL_STAGE -} - ptw_stage_q, ptw_stage_d; - -// 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 ASIDs -logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; -// register the VPN we need to walk, SV39 defines a 39 bit virtual address -logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; -logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; -// register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage -logic [riscv::GPLEN-1:0] gpaddr_q, gpaddr_n, gpaddr_base; -logic [PT_LEVELS-2:0][riscv::GPLEN-1:0] gpaddr; -// 4 byte aligned physical pointer -logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; -logic [riscv::PLEN-1:0] gptw_pptr_q, gptw_pptr_n; - -// Assignments -assign update_vaddr_o = vaddr_q; - -assign ptw_active_o = (state_q != IDLE); -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; - -// ----------- -// TLB Update -// ----------- - -assign gpaddr_base = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; - -genvar z, w; -generate - for (z = 0; z < PT_LEVELS - 1; z++) begin - - // 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[z] = (ptw_lvl_q[0] == (z)) && (pte[0].ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-z)-1:0] != '0); - - //record the vaddr corresponding to each level - for (w = 0; w < HYP_EXT * 2 + 1; w++) begin - assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))] : + // input registers + logic data_rvalid_q; + riscv::xlen_t data_rdata_q; + + pte_cva6_t [HYP_EXT*2:0] pte; //[gpte_d,gpte_q,pte] + // register to perform context switch between stages + // pte_cva6_t gpte_q, gpte_d; + assign pte[0] = pte_cva6_t'(data_rdata_q[riscv::PPNW+9:0]); + + enum logic [2:0] { + IDLE, + WAIT_GRANT, + PTE_LOOKUP, + WAIT_RVALID, + PROPAGATE_ERROR, + PROPAGATE_ACCESS_ERROR, + LATENCY + } + state_q, state_d; + + logic [PT_LEVELS-1:0] misaligned_page; + logic [HYP_EXT:0][PT_LEVELS-2:0] ptw_lvl_n, ptw_lvl_q; + + // define 3 PTW stages to be used in sv39x4. sv32 and sv39 are always in S_STAGE + // S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs + // G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp) + // G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp) + enum logic [1:0] { + S_STAGE, + G_INTERMED_STAGE, + G_FINAL_STAGE + } + ptw_stage_q, ptw_stage_d; + + // 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 ASIDs + logic [HYP_EXT:0][ASID_WIDTH[0]-1:0] tlb_update_asid_q, tlb_update_asid_n; + // register the VPN we need to walk, SV39 defines a 39 bit virtual address + logic [riscv::VLEN-1:0] vaddr_q, vaddr_n; + logic [HYP_EXT*2:0][PT_LEVELS-2:0][(VPN_LEN/PT_LEVELS)-1:0] vaddr_lvl; + // register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage + logic [riscv::GPLEN-1:0] gpaddr_q, gpaddr_n, gpaddr_base; + logic [PT_LEVELS-2:0][riscv::GPLEN-1:0] gpaddr; + // 4 byte aligned physical pointer + logic [riscv::PLEN-1:0] ptw_pptr_q, ptw_pptr_n; + logic [riscv::PLEN-1:0] gptw_pptr_q, gptw_pptr_n; + + // Assignments + assign update_vaddr_o = vaddr_q; + + assign ptw_active_o = (state_q != IDLE); + 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; + + // ----------- + // TLB Update + // ----------- + + assign gpaddr_base = {pte[0].ppn[riscv::GPPNW-1:0], vaddr_q[11:0]}; + + genvar z, w; + generate + for (z = 0; z < PT_LEVELS - 1; z++) begin + + // 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[z] = (ptw_lvl_q[0] == (z)) && (pte[0].ppn[(VPN_LEN/PT_LEVELS)*(PT_LEVELS-1-z)-1:0] != '0); + + //record the vaddr corresponding to each level + for (w = 0; w < HYP_EXT * 2 + 1; w++) begin + assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))] : w==1 ? gptw_pptr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]: gpaddr_q[12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-1))-1:12+((VPN_LEN/PT_LEVELS)*(PT_LEVELS-z-2))]; - end + end - assign gpaddr[z][VPN_LEN-(VPN_LEN/PT_LEVELS):0]= (ptw_lvl_q[0] == z) ? vaddr_q[VPN_LEN-(VPN_LEN/PT_LEVELS):0] : gpaddr_base[VPN_LEN-(VPN_LEN/PT_LEVELS):0]; - assign gpaddr[z][VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1] : gpaddr_base[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]; - assign gpaddr[z][riscv::GPLEN-1:VPN_LEN+1] = gpaddr_base[riscv::GPLEN-1:VPN_LEN+1]; + assign gpaddr[z][VPN_LEN-(VPN_LEN/PT_LEVELS):0]= (ptw_lvl_q[0] == z) ? vaddr_q[VPN_LEN-(VPN_LEN/PT_LEVELS):0] : gpaddr_base[VPN_LEN-(VPN_LEN/PT_LEVELS):0]; + assign gpaddr[z][VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1] : gpaddr_base[VPN_LEN:VPN_LEN-(VPN_LEN/PT_LEVELS)+1]; + assign gpaddr[z][riscv::GPLEN-1:VPN_LEN+1] = gpaddr_base[riscv::GPLEN-1:VPN_LEN+1]; - end -endgenerate + end + endgenerate -always_comb begin : tlb_update + always_comb begin : tlb_update // update the correct page table level for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin From 50e0c4bdb7cda73ad9803d4bcc12ef06bf78e060 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 18:07:26 +0100 Subject: [PATCH 179/182] linting --- core/mmu_unify/cva6_ptw.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index c5355cd1ce..17f2c8df8d 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -173,7 +173,6 @@ generate endgenerate always_comb begin : tlb_update - // update the correct page table level for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin for (int unsigned x = 0; x < PT_LEVELS - 1; x++) begin From 4e7b61dee3be45084ac7cf0786fa36030280192a Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 18:34:56 +0100 Subject: [PATCH 180/182] verible format ptw --- core/mmu_unify/cva6_ptw.sv | 754 ++++++++++++++++++------------------- 1 file changed, 377 insertions(+), 377 deletions(-) diff --git a/core/mmu_unify/cva6_ptw.sv b/core/mmu_unify/cva6_ptw.sv index a6b12dd854..f6e278bf24 100644 --- a/core/mmu_unify/cva6_ptw.sv +++ b/core/mmu_unify/cva6_ptw.sv @@ -172,432 +172,432 @@ module cva6_ptw end endgenerate -always_comb begin : tlb_update - // update the correct page table level - for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin - for (int unsigned x = 0; x < PT_LEVELS - 1; x++) begin - if((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0])&& HYP_EXT==1) begin - shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT?0 : 1] == x); - end else if (enable_translation_i[0] || en_ld_st_translation_i[0] || HYP_EXT == 0) begin - shared_tlb_update_o.is_page[x][y] = y == 0 ? (ptw_lvl_q[0] == x) : 1'b0; + always_comb begin : tlb_update + // update the correct page table level + for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin + for (int unsigned x = 0; x < PT_LEVELS - 1; x++) begin + if((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0])&& HYP_EXT==1) begin + shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT?0 : 1] == x); + end else if (enable_translation_i[0] || en_ld_st_translation_i[0] || HYP_EXT == 0) begin + shared_tlb_update_o.is_page[x][y] = y == 0 ? (ptw_lvl_q[0] == x) : 1'b0; + end else begin + shared_tlb_update_o.is_page[x][y] = y != 0 ? (ptw_lvl_q[0] == x) : 1'b0; + end + end + + // set the global mapping bit + if ((enable_translation_i[HYP_EXT] || en_ld_st_translation_i[HYP_EXT]) && HYP_EXT == 1) begin + shared_tlb_update_o.content[y] = y == 0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; end else begin - shared_tlb_update_o.is_page[x][y] = y != 0 ? (ptw_lvl_q[0] == x) : 1'b0; + shared_tlb_update_o.content[y] = y == 0 ? (pte[0] | (global_mapping_q << 5)) : '0; end end + // output the correct ASIDs + shared_tlb_update_o.asid = tlb_update_asid_q; - // set the global mapping bit - if ((enable_translation_i[HYP_EXT] || en_ld_st_translation_i[HYP_EXT]) && HYP_EXT == 1) begin - shared_tlb_update_o.content[y] = y == 0 ? pte[HYP_EXT] | (global_mapping_q << 5) : pte[0]; - end else begin - shared_tlb_update_o.content[y] = y == 0 ? (pte[0] | (global_mapping_q << 5)) : '0; - end + bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; + if (HYP_EXT == 1) + bad_paddr_o[HYP_EXT][riscv::GPLEN:0] = ptw_error_o[HYP_EXT] ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[riscv::GPLEN:0] : gpaddr_q) : 'b0; end - // output the correct ASIDs - shared_tlb_update_o.asid = tlb_update_asid_q; - - bad_paddr_o[0] = ptw_access_exception_o ? ptw_pptr_q : 'b0; - if (HYP_EXT == 1) - bad_paddr_o[HYP_EXT][riscv::GPLEN:0] = ptw_error_o[HYP_EXT] ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[riscv::GPLEN:0] : gpaddr_q) : 'b0; -end - -assign req_port_o.tag_valid = tag_valid_q; - -logic allow_access; - - - -pmp #( - .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 = 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 -); - -assign shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); - -//------------------- -// 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, or if any bits or encodings -// that are reserved for future standard use are set within pte, stop and raise -// a page-fault exception corresponding to the original access type. -// 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 - automatic logic [riscv::PLEN-1:0] pptr; - // automatic logic [riscv::GPLEN-1:0] gpaddr; - // default assignments - // 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_we = 1'b0; - ptw_error_o = '0; - 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; - gptw_pptr_n = gptw_pptr_q; - state_d = state_q; - ptw_stage_d = ptw_stage_q; - global_mapping_n = global_mapping_q; - // input registers - tlb_update_asid_n = tlb_update_asid_q; - vaddr_n = vaddr_q; - gpaddr_n = gpaddr_q; - pptr = ptw_pptr_q; - // gpaddr = gpaddr_q; - - shared_tlb_miss_o = 1'b0; - - if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[HYP_EXT]; - - case (state_q) - - IDLE: begin - // by default we start with the top-most page table - ptw_lvl_n = '0; - global_mapping_n = 1'b0; - is_instr_ptw_n = 1'b0; - gpaddr_n = '0; - - if (HYP_EXT == 1) pte[HYP_EXT*2] = '0; - // if we got an ITLB miss - if (((|enable_translation_i[HYP_EXT:0]) || |en_ld_st_translation_i[HYP_EXT:0]) && shared_tlb_access_i && ~shared_tlb_hit_i) begin - if ((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0]) && HYP_EXT==1) begin - ptw_stage_d = G_INTERMED_STAGE; - pptr = { - satp_ppn_i[HYP_EXT], - shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], - (PT_LEVELS)'(0) - }; - gptw_pptr_n = pptr; - ptw_pptr_n = { - satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], - pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], - (PT_LEVELS)'(0) - }; - end else if (((|enable_translation_i[HYP_EXT:0] && !enable_translation_i[0]) || (|en_ld_st_translation_i[HYP_EXT:0] && !en_ld_st_translation_i[0])) && HYP_EXT==1) begin - ptw_stage_d = G_FINAL_STAGE; - gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; - ptw_pptr_n = { - satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], - shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], - (PT_LEVELS)'(0) - }; - end else begin - ptw_stage_d = S_STAGE; - if((enable_translation_i[HYP_EXT*2] || en_ld_st_translation_i[HYP_EXT*2]) && HYP_EXT==1) - ptw_pptr_n = { + assign req_port_o.tag_valid = tag_valid_q; + + logic allow_access; + + + + pmp #( + .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 = 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 + ); + + assign shared_tlb_update_o.vpn = VPN_LEN'(vaddr_q[riscv::SV+HYP_EXT*2-1:12]); + + //------------------- + // 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, or if any bits or encodings + // that are reserved for future standard use are set within pte, stop and raise + // a page-fault exception corresponding to the original access type. + // 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 + automatic logic [riscv::PLEN-1:0] pptr; + // automatic logic [riscv::GPLEN-1:0] gpaddr; + // default assignments + // 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_we = 1'b0; + ptw_error_o = '0; + 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; + gptw_pptr_n = gptw_pptr_q; + state_d = state_q; + ptw_stage_d = ptw_stage_q; + global_mapping_n = global_mapping_q; + // input registers + tlb_update_asid_n = tlb_update_asid_q; + vaddr_n = vaddr_q; + gpaddr_n = gpaddr_q; + pptr = ptw_pptr_q; + // gpaddr = gpaddr_q; + + shared_tlb_miss_o = 1'b0; + + if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[HYP_EXT]; + + case (state_q) + + IDLE: begin + // by default we start with the top-most page table + ptw_lvl_n = '0; + global_mapping_n = 1'b0; + is_instr_ptw_n = 1'b0; + gpaddr_n = '0; + + if (HYP_EXT == 1) pte[HYP_EXT*2] = '0; + // if we got an ITLB miss + if (((|enable_translation_i[HYP_EXT:0]) || |en_ld_st_translation_i[HYP_EXT:0]) && shared_tlb_access_i && ~shared_tlb_hit_i) begin + if ((&enable_translation_i[HYP_EXT:0] || &en_ld_st_translation_i[HYP_EXT:0]) && HYP_EXT==1) begin + ptw_stage_d = G_INTERMED_STAGE; + pptr = { satp_ppn_i[HYP_EXT], shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0) }; - else + gptw_pptr_n = pptr; ptw_pptr_n = { - satp_ppn_i[0], - shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0) }; - end + end else if (((|enable_translation_i[HYP_EXT:0] && !enable_translation_i[0]) || (|en_ld_st_translation_i[HYP_EXT:0] && !en_ld_st_translation_i[0])) && HYP_EXT==1) begin + ptw_stage_d = G_FINAL_STAGE; + gpaddr_n = shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:0]; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + shared_tlb_vaddr_i[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + end else begin + ptw_stage_d = S_STAGE; + if((enable_translation_i[HYP_EXT*2] || en_ld_st_translation_i[HYP_EXT*2]) && HYP_EXT==1) + ptw_pptr_n = { + satp_ppn_i[HYP_EXT], + shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + else + ptw_pptr_n = { + satp_ppn_i[0], + shared_tlb_vaddr_i[riscv::SV-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + end - is_instr_ptw_n = itlb_req_i; - vaddr_n = shared_tlb_vaddr_i; - state_d = WAIT_GRANT; - shared_tlb_miss_o = 1'b1; + is_instr_ptw_n = itlb_req_i; + vaddr_n = shared_tlb_vaddr_i; + state_d = WAIT_GRANT; + shared_tlb_miss_o = 1'b1; - for (int unsigned b = 0; b < HYP_EXT + 1; b++) begin - tlb_update_asid_n[b] = b==0 ? ((enable_translation_i[2*HYP_EXT] || en_ld_st_translation_i[2*HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + for (int unsigned b = 0; b < HYP_EXT + 1; b++) begin + tlb_update_asid_n[b] = b==0 ? ((enable_translation_i[2*HYP_EXT] || en_ld_st_translation_i[2*HYP_EXT]) ? asid_i[HYP_EXT] : asid_i[0]) : asid_i[HYP_EXT*2]; + end end 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; + 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 - 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[0].g && ptw_stage_q == S_STAGE) 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[0].v || (!pte[0].r && pte[0].w)) // || (|pte.reserved)) - state_d = PROPAGATE_ERROR; - // ----------- - // Valid PTE - // ----------- - else begin - state_d = LATENCY; - // it is a valid PTE - // if pte.r = 1 or pte.x = 1 it is a valid PTE - if (pte[0].r || pte[0].x) begin - case (ptw_stage_q) - S_STAGE: begin - if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin - state_d = WAIT_GRANT; - ptw_stage_d = G_FINAL_STAGE; - if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[0]; - ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; - gpaddr_n = gpaddr[ptw_lvl_q[0]]; - ptw_pptr_n = { - satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], - gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], - (PT_LEVELS)'(0) - }; - ptw_lvl_n[0] = 0; - end - end - G_INTERMED_STAGE: begin - state_d = WAIT_GRANT; - ptw_stage_d = S_STAGE; - ptw_lvl_n[0] = ptw_lvl_q[HYP_EXT]; - pptr = {pte[0].ppn[riscv::GPPNW-1:0], gptw_pptr_q[11:0]}; - if (ptw_lvl_q[0] == 1) pptr[20:0] = gptw_pptr_q[20:0]; - if (ptw_lvl_q[0] == 0) pptr[29:0] = gptw_pptr_q[29:0]; - ptw_pptr_n = pptr; - end - default: ; - endcase - // Valid translation found (either 1G, 2M 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[0].x || !pte[0].a) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) - 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin - if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) - shared_tlb_update_o.valid = 1'b1; - end else begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - 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[0].w || !pte[0].d)) begin - shared_tlb_update_o.valid = 1'b0; - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - end - end - - //if there is a misaligned page, propagate error - if (|misaligned_page) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - shared_tlb_update_o.valid = 1'b0; - end - - // check if 63:41 are all zeros - if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = G_FINAL_STAGE; - end - // this is a pointer to the next TLB level - end else begin - // pointer to next level of page table - - if (ptw_lvl_q[0] == PT_LEVELS - 1) begin - // Should already be the last level page table => Error - ptw_lvl_n[0] = PT_LEVELS - 1; - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - - - end else begin - ptw_lvl_n[0] = ptw_lvl_q[0] + 1; - state_d = WAIT_GRANT; + PTE_LOOKUP: begin + // we wait for the valid signal + if (data_rvalid_q) begin + + // check if the global mapping bit is set + if (pte[0].g && ptw_stage_q == S_STAGE) 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[0].v || (!pte[0].r && pte[0].w)) // || (|pte.reserved)) + state_d = PROPAGATE_ERROR; + // ----------- + // Valid PTE + // ----------- + else begin + state_d = LATENCY; + // it is a valid PTE + // if pte.r = 1 or pte.x = 1 it is a valid PTE + if (pte[0].r || pte[0].x) begin case (ptw_stage_q) S_STAGE: begin if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin - ptw_stage_d = G_INTERMED_STAGE; + state_d = WAIT_GRANT; + ptw_stage_d = G_FINAL_STAGE; if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[0]; - ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1; - pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - gptw_pptr_n = pptr; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0]; + gpaddr_n = gpaddr[ptw_lvl_q[0]]; ptw_pptr_n = { satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], - pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + gpaddr[ptw_lvl_q[0]][riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], (PT_LEVELS)'(0) }; ptw_lvl_n[0] = 0; - end else begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; end end G_INTERMED_STAGE: begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; - end - G_FINAL_STAGE: begin - ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + state_d = WAIT_GRANT; + ptw_stage_d = S_STAGE; + ptw_lvl_n[0] = ptw_lvl_q[HYP_EXT]; + pptr = {pte[0].ppn[riscv::GPPNW-1:0], gptw_pptr_q[11:0]}; + if (ptw_lvl_q[0] == 1) pptr[20:0] = gptw_pptr_q[20:0]; + if (ptw_lvl_q[0] == 0) pptr[29:0] = gptw_pptr_q[29:0]; + ptw_pptr_n = pptr; end + default: ; endcase + // Valid translation found (either 1G, 2M 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[0].x || !pte[0].a) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end else if((ptw_stage_q == G_FINAL_STAGE) || !enable_translation_i[HYP_EXT] || HYP_EXT==0) + 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[0].a && ((pte[0].r && !hlvx_inst_i) || (pte[0].x && (mxr_i[0] || hlvx_inst_i || (ptw_stage_q == S_STAGE && mxr_i[HYP_EXT] && en_ld_st_translation_i[HYP_EXT*2] && HYP_EXT==1))))) begin + if((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_translation_i[HYP_EXT] || HYP_EXT==0) + shared_tlb_update_o.valid = 1'b1; + end else begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + 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[0].w || !pte[0].d)) begin + shared_tlb_update_o.valid = 1'b0; + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + end - if (HYP_EXT == 1 && (pte[0].a || pte[0].d || pte[0].u)) begin + //if there is a misaligned page, propagate error + if (|misaligned_page) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + shared_tlb_update_o.valid = 1'b0; + end + + // check if 63:41 are all zeros + if (HYP_EXT==1 && ((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-HYP_EXT:riscv::GPPNW]) == 1'b0)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = G_FINAL_STAGE; + end + // this is a pointer to the next TLB level + end else begin + // pointer to next level of page table + + if (ptw_lvl_q[0] == PT_LEVELS - 1) begin + // Should already be the last level page table => Error + ptw_lvl_n[0] = PT_LEVELS - 1; state_d = PROPAGATE_ERROR; ptw_stage_d = ptw_stage_q; + + + end else begin + ptw_lvl_n[0] = ptw_lvl_q[0] + 1; + state_d = WAIT_GRANT; + + case (ptw_stage_q) + S_STAGE: begin + if (HYP_EXT==1 && ((is_instr_ptw_q && enable_translation_i[HYP_EXT]) || (!is_instr_ptw_q && en_ld_st_translation_i[HYP_EXT]))) begin + ptw_stage_d = G_INTERMED_STAGE; + if (HYP_EXT == 1) pte[HYP_EXT*2] = pte[0]; + ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1; + pptr = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + gptw_pptr_n = pptr; + ptw_pptr_n = { + satp_ppn_i[HYP_EXT*2][riscv::PPNW-1:2], + pptr[riscv::SV+HYP_EXT*2-1:riscv::SV-(VPN_LEN/PT_LEVELS)], + (PT_LEVELS)'(0) + }; + ptw_lvl_n[0] = 0; + end else begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[0][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + end + G_INTERMED_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + G_FINAL_STAGE: begin + ptw_pptr_n = {pte[0].ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (PT_LEVELS)'(0)}; + end + endcase + + if (HYP_EXT == 1 && (pte[0].a || pte[0].d || pte[0].u)) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end + end + // check if 63:41 are all zeros + if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin + state_d = PROPAGATE_ERROR; + ptw_stage_d = ptw_stage_q; + end end + end - // check if 63:41 are all zeros - if (HYP_EXT==1 && (((enable_translation_i[HYP_EXT*2] && is_instr_ptw_q) || (en_ld_st_translation_i[HYP_EXT*2] && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte[0].ppn[riscv::PPNW-1:riscv::GPPNW]) == 1'b0))) begin - state_d = PROPAGATE_ERROR; - ptw_stage_d = ptw_stage_q; - 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; + ptw_stage_d = ptw_stage_q; + state_d = PROPAGATE_ACCESS_ERROR; 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; - ptw_stage_d = ptw_stage_q; - state_d = PROPAGATE_ACCESS_ERROR; + // 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[0] = 1'b1; + if (HYP_EXT == 1) begin + ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; + ptw_error_o[HYP_EXT*2] = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0; 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[0] = 1'b1; - if (HYP_EXT == 1) begin - ptw_error_o[HYP_EXT] = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0; - ptw_error_o[HYP_EXT*2] = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0; + 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 - 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; - ptw_stage_q <= S_STAGE; - is_instr_ptw_q <= 1'b0; - ptw_lvl_q <= '0; - tag_valid_q <= 1'b0; - tlb_update_asid_q <= '0; - vaddr_q <= '0; - gpaddr_q <= '0; - ptw_pptr_q <= '0; - gptw_pptr_q <= '0; - global_mapping_q <= 1'b0; - data_rdata_q <= '0; - data_rvalid_q <= 1'b0; - if (HYP_EXT == 1) pte[HYP_EXT] <= '0; - end else begin - state_q <= state_d; - ptw_stage_q <= ptw_stage_d; - ptw_pptr_q <= ptw_pptr_n; - gptw_pptr_q <= gptw_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; - gpaddr_q <= gpaddr_n; - global_mapping_q <= global_mapping_n; - data_rdata_q <= req_port_i.data_rdata; - data_rvalid_q <= req_port_i.data_rvalid; - - if (HYP_EXT == 1) pte[HYP_EXT] <= pte[HYP_EXT*2]; + + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + state_q <= IDLE; + ptw_stage_q <= S_STAGE; + is_instr_ptw_q <= 1'b0; + ptw_lvl_q <= '0; + tag_valid_q <= 1'b0; + tlb_update_asid_q <= '0; + vaddr_q <= '0; + gpaddr_q <= '0; + ptw_pptr_q <= '0; + gptw_pptr_q <= '0; + global_mapping_q <= 1'b0; + data_rdata_q <= '0; + data_rvalid_q <= 1'b0; + if (HYP_EXT == 1) pte[HYP_EXT] <= '0; + end else begin + state_q <= state_d; + ptw_stage_q <= ptw_stage_d; + ptw_pptr_q <= ptw_pptr_n; + gptw_pptr_q <= gptw_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; + gpaddr_q <= gpaddr_n; + global_mapping_q <= global_mapping_n; + data_rdata_q <= req_port_i.data_rdata; + data_rvalid_q <= req_port_i.data_rvalid; + + if (HYP_EXT == 1) pte[HYP_EXT] <= pte[HYP_EXT*2]; + end end -end endmodule /* verilator lint_on WIDTH */ From d03f5c0731586379cd4187d5578c54ad8fbe8b80 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 18:49:44 +0100 Subject: [PATCH 181/182] verible format shared tlb --- core/mmu_unify/cva6_shared_tlb.sv | 728 +++++++++++++++--------------- 1 file changed, 361 insertions(+), 367 deletions(-) diff --git a/core/mmu_unify/cva6_shared_tlb.sv b/core/mmu_unify/cva6_shared_tlb.sv index 92f0407da2..7f24856ede 100644 --- a/core/mmu_unify/cva6_shared_tlb.sv +++ b/core/mmu_unify/cva6_shared_tlb.sv @@ -22,24 +22,23 @@ /* verilator lint_off WIDTH */ -module cva6_shared_tlb -#( - parameter type pte_cva6_t = logic, - parameter type tlb_update_cva6_t = logic, - parameter int SHARED_TLB_DEPTH = 64, - parameter int SHARED_TLB_WAYS = 2, - parameter int unsigned HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - parameter int unsigned VPN_LEN = 1, - parameter int unsigned PT_LEVELS = 1 +module cva6_shared_tlb #( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int SHARED_TLB_DEPTH = 64, + parameter int SHARED_TLB_WAYS = 2, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH[HYP_EXT:0] = {1}, //[vmid_width,asid_width] + parameter int unsigned VPN_LEN = 1, + parameter int unsigned PT_LEVELS = 1 ) ( - input logic clk_i, // Clock + 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 [1:0][HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled + input logic [1:0][HYP_EXT*2:0] v_st_enbl_i, // v_i,g-stage enabled, s-stage enabled - input logic [ASID_WIDTH[0]-1:0] dtlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] - input logic [ASID_WIDTH[0]-1:0] itlb_asid_i[HYP_EXT:0],//[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] dtlb_asid_i[HYP_EXT:0], //[vmid,vs_asid,asid] + input logic [ASID_WIDTH[0]-1:0] itlb_asid_i[HYP_EXT:0], //[vmid,vs_asid,asid] // from TLBs // did we miss? @@ -70,404 +69,399 @@ module cva6_shared_tlb ); -tlb_update_cva6_t shared_tlb_update_delayed,shared_tlb_update_delayed2; -logic shared_tlb_update_valid_delayed,shared_tlb_update_valid_delayed2; + tlb_update_cva6_t shared_tlb_update_delayed, shared_tlb_update_delayed2; + logic shared_tlb_update_valid_delayed, shared_tlb_update_valid_delayed2; -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 [HYP_EXT:0][ASID_WIDTH[0]-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 -} shared_tag_t; + typedef struct packed { + logic [HYP_EXT:0][ASID_WIDTH[0]-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 + } 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(pte_cva6_t)-1:0] pte_wr_data [HYP_EXT:0]; + 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 [ HYP_EXT:0]; -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][HYP_EXT:0]; + 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] [HYP_EXT: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+HYP_EXT-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; + logic [PT_LEVELS+HYP_EXT-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; -logic [SHARED_TLB_WAYS-1:0][HYP_EXT:0] match_asid; -logic [SHARED_TLB_WAYS-1:0] match_stage; + logic [SHARED_TLB_WAYS-1:0][HYP_EXT:0] match_asid; + logic [SHARED_TLB_WAYS-1:0] match_stage; -pte_cva6_t [SHARED_TLB_WAYS-1:0][HYP_EXT:0] pte; + pte_cva6_t [SHARED_TLB_WAYS-1:0][HYP_EXT: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[0]-1:0] tlb_update_asid_q [HYP_EXT:0], tlb_update_asid_d[HYP_EXT:0]; + logic [ASID_WIDTH[0]-1:0] tlb_update_asid_q[HYP_EXT:0], tlb_update_asid_d[HYP_EXT:0]; -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; -int i_req_d,i_req_q; + int i_req_d, i_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; -genvar i,x; + genvar i, x; generate - for (i=0; i < SHARED_TLB_WAYS; i++) begin + for (i = 0; i < SHARED_TLB_WAYS; i++) begin : gen_match_tlb_ways //identify page_match for all TLB Entries - for (x=0; x < PT_LEVELS; x++) begin - assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[i_req_q][HYP_EXT:0])): - ((&v_st_enbl_i[i_req_q][HYP_EXT:0]) ? - ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) + for (x = 0; x < PT_LEVELS; x++) begin : gen_match + assign page_match[i][x] = x==0 ? 1 :((HYP_EXT==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 + &(shared_tag_rd[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl_i[i_req_q][HYP_EXT:0])): + ((&v_st_enbl_i[i_req_q][HYP_EXT:0]) ? + ((shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][HYP_EXT] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT])) || (shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (shared_tag_rd[i].is_page[PT_LEVELS-2-x][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][0]))): shared_tag_rd[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl_i[i_req_q][0] || shared_tag_rd[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl_i[i_req_q][HYP_EXT])); - //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[i_req_q][0]) ? // - vpn_q[x] == shared_tag_rd[i].vpn[x] && vpn_q[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // - vpn_q[x] == shared_tag_rd[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 vpn matches at all PT levels for all TLB entries + assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl_i[i_req_q][0]) ? // + vpn_q[x] == shared_tag_rd[i].vpn[x] && vpn_q[x+1][(VPN_LEN%PT_LEVELS)-1:0] == shared_tag_rd[i].vpn[x+1][(VPN_LEN%PT_LEVELS)-1:0]: // + vpn_q[x] == shared_tag_rd[i].vpn[x]; - end - end -endgenerate + //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]; -genvar w; - generate - for (w=0; w < PT_LEVELS; w++) begin - assign vpn_d[w] = ((|v_st_enbl_i[1][HYP_EXT:0]) && 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)] : // - (((|v_st_enbl_i[0][HYP_EXT:0]) && 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 + end + endgenerate + + genvar w; + generate + for (w = 0; w < PT_LEVELS; w++) begin + assign vpn_d[w] = ((|v_st_enbl_i[1][HYP_EXT:0]) && 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)] : // + (((|v_st_enbl_i[0][HYP_EXT:0]) && 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 - if(HYP_EXT==1) - //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 + if (HYP_EXT == 1) //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 assign vpn_d[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = ((|v_st_enbl_i[1][HYP_EXT:0]) && itlb_access_i && ~itlb_hit_i && ~dtlb_access_i) ? // - itlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : // - (((|v_st_enbl_i[0][HYP_EXT:0]) && dtlb_access_i && ~dtlb_hit_i)? // - dtlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : vpn_q[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]); - -/////////////////////////////////////////////////////// -// tag comparison, hit generation -/////////////////////////////////////////////////////// - always_comb begin : itlb_dtlb_miss - itlb_miss_o = 1'b0; - dtlb_miss_o = 1'b0; - - 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; - i_req_d = i_req_q; - - // if we got an ITLB miss - if ((|v_st_enbl_i[1][HYP_EXT:0]) & 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)]; - - itlb_miss_o = 1'b1; - itlb_req_d = 1'b1; - tlb_update_asid_d = itlb_asid_i; - - shared_tlb_access_d = '1; - shared_tlb_vaddr_d = itlb_vaddr_i; - i_req_d = 1; - - // we got an DTLB miss - end else if ((|v_st_enbl_i[0][HYP_EXT:0]) & 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; - tlb_update_asid_d = dtlb_asid_i; - - shared_tlb_access_d = '1; - shared_tlb_vaddr_d = dtlb_vaddr_i; - i_req_d = 0; - 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 - // first level match, this may be a giga 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) - match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req_q][0]) || !v_st_enbl_i[i_req_q][0]; - - if(HYP_EXT==1) begin - match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req_q][HYP_EXT]) || !v_st_enbl_i[i_req_q][HYP_EXT]; - end - - // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off - match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req_q]; - - if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin - if (|level_match[i]) 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.content = pte[i]; - itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - itlb_update_o.asid[a] = tlb_update_asid_q[a]; - end - 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.content = pte[i]; - dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; - for (int unsigned a = 0; a < HYP_EXT+1; a++) begin - dtlb_update_o.asid[a] = tlb_update_asid_q[a]; - 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 <= '{default: 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; - i_req_q <= 0; - shared_tag_valid <= '0; - shared_tlb_update_valid_delayed<='0; - shared_tlb_update_valid_delayed2<='0; - shared_tlb_update_delayed<='0; - shared_tlb_update_delayed2<='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; - i_req_q <= i_req_d; - shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; - - if(shared_tlb_update_i.valid) begin - shared_tlb_update_valid_delayed <=shared_tlb_update_i.valid; - shared_tlb_update_delayed <= shared_tlb_update_i; - end - - - shared_tlb_update_valid_delayed2 <= shared_tlb_update_valid_delayed; - shared_tlb_update_delayed2 <= shared_tlb_update_delayed; + itlb_vaddr_i[VPN_LEN-1:VPN_LEN-(VPN_LEN%PT_LEVELS)] : // + (((|v_st_enbl_i[0][HYP_EXT:0]) && dtlb_access_i && ~dtlb_hit_i) ? // + dtlb_vaddr_i[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)] : vpn_q[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0]); + + /////////////////////////////////////////////////////// + // tag comparison, hit generation + /////////////////////////////////////////////////////// + always_comb begin : itlb_dtlb_miss + itlb_miss_o = 1'b0; + dtlb_miss_o = 1'b0; + + 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; + i_req_d = i_req_q; + + // if we got an ITLB miss + if ((|v_st_enbl_i[1][HYP_EXT:0]) & 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)]; + + itlb_miss_o = 1'b1; + itlb_req_d = 1'b1; + tlb_update_asid_d = itlb_asid_i; + + shared_tlb_access_d = '1; + shared_tlb_vaddr_d = itlb_vaddr_i; + i_req_d = 1; + + // we got an DTLB miss + end else if ((|v_st_enbl_i[0][HYP_EXT:0]) & 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; + tlb_update_asid_d = dtlb_asid_i; + + shared_tlb_access_d = '1; + shared_tlb_vaddr_d = dtlb_vaddr_i; + i_req_d = 0; + 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 + // first level match, this may be a giga 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) + match_asid[i][0] = (((tlb_update_asid_q[0][ASID_WIDTH[0]-1:0] == shared_tag_rd[i].asid[0][ASID_WIDTH[0]-1:0]) || pte[i][0].g) && v_st_enbl_i[i_req_q][0]) || !v_st_enbl_i[i_req_q][0]; + + if (HYP_EXT == 1) begin + match_asid[i][HYP_EXT] = (tlb_update_asid_q[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] == shared_tag_rd[i].asid[HYP_EXT][ASID_WIDTH[HYP_EXT]-1:0] && v_st_enbl_i[i_req_q][HYP_EXT]) || !v_st_enbl_i[i_req_q][HYP_EXT]; + end + // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off + match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl_i[i_req_q]; + + if (shared_tag_valid[i] && &match_asid[i] && match_stage[i]) begin + if (|level_match[i]) 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.content = pte[i]; + itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; + for (int unsigned a = 0; a < HYP_EXT + 1; a++) begin + itlb_update_o.asid[a] = tlb_update_asid_q[a]; + end + 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.content = pte[i]; + dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl; + for (int unsigned a = 0; a < HYP_EXT + 1; a++) begin + dtlb_update_o.asid[a] = tlb_update_asid_q[a]; 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 <= '{default: 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; + i_req_q <= 0; + shared_tag_valid <= '0; + shared_tlb_update_valid_delayed <= '0; + shared_tlb_update_valid_delayed2 <= '0; + shared_tlb_update_delayed <= '0; + shared_tlb_update_delayed2 <= '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; + i_req_q <= i_req_d; + shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; + + if (shared_tlb_update_i.valid) begin + shared_tlb_update_valid_delayed <= shared_tlb_update_i.valid; + shared_tlb_update_delayed <= shared_tlb_update_i; + 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 + shared_tlb_update_valid_delayed2 <= shared_tlb_update_valid_delayed; + shared_tlb_update_delayed2 <= shared_tlb_update_delayed; + 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; - assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; - assign shared_tag_wr.v_st_enbl = v_st_enbl_i[i_req_q]; + assign shared_tag_wr.asid = shared_tlb_update_i.asid; + assign shared_tag_wr.is_page = shared_tlb_update_i.is_page; + assign shared_tag_wr.v_st_enbl = v_st_enbl_i[i_req_q]; - 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 - if(HYP_EXT==1) begin - //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign shared_tag_wr.vpn[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = shared_tlb_update_i.vpn[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)]; - end - endgenerate + genvar z; + generate + for (z = 0; z < PT_LEVELS; z++) begin : gen_shared_tag + assign shared_tag_wr.vpn[z] = shared_tlb_update_i.vpn[((VPN_LEN/PT_LEVELS)*(z+1))-1:((VPN_LEN/PT_LEVELS)*z)]; + end + if (HYP_EXT == 1) begin : gen_shared_tag_hyp + //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 + assign shared_tag_wr.vpn[PT_LEVELS][(VPN_LEN%PT_LEVELS)-1:0] = shared_tlb_update_i.vpn[VPN_LEN-1: VPN_LEN-(VPN_LEN%PT_LEVELS)]; + end + endgenerate - 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_addr = shared_tlb_update_i.vpn[$clog2(SHARED_TLB_DEPTH)-1:0]; - genvar h; - generate - for (h=0; h < HYP_EXT+1; h++) begin - assign pte_wr_data[h] = shared_tlb_update_i.content[h]; - end - endgenerate - - - 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]); - - for (genvar a = 0; a < HYP_EXT+1; a++) begin : content_sram - // PTE RAM - sram #( - .DATA_WIDTH($bits(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[a]), - .be_i ('1), - .ruser_o(), - .rdata_o(pte_rd_data[i][a]) - ); - assign pte[i][a] = pte_cva6_t'(pte_rd_data[i][a]); - end - end + genvar h; + generate + for (h = 0; h < HYP_EXT + 1; h++) begin : gen_pte_wr_data + assign pte_wr_data[h] = shared_tlb_update_i.content[h]; + end + endgenerate + + 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]); + + for (genvar a = 0; a < HYP_EXT + 1; a++) begin : g_content_sram + // PTE RAM + sram #( + .DATA_WIDTH($bits(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[a]), + .be_i ('1), + .ruser_o(), + .rdata_o(pte_rd_data[i][a]) + ); + assign pte[i][a] = pte_cva6_t'(pte_rd_data[i][a]); + end + end endmodule -/* verilator lint_on WIDTH */ \ No newline at end of file +/* verilator lint_on WIDTH */ From 75ae9fc9cfeca51a95db27e62af5af883b3febb2 Mon Sep 17 00:00:00 2001 From: AngelaGonzalezMarino Date: Mon, 19 Feb 2024 18:51:00 +0100 Subject: [PATCH 182/182] veribla tlb format --- core/mmu_unify/cva6_tlb.sv | 641 +++++++++++++++++++------------------ 1 file changed, 335 insertions(+), 306 deletions(-) diff --git a/core/mmu_unify/cva6_tlb.sv b/core/mmu_unify/cva6_tlb.sv index e8642e58b4..bb39a8e8d6 100644 --- a/core/mmu_unify/cva6_tlb.sv +++ b/core/mmu_unify/cva6_tlb.sv @@ -22,301 +22,316 @@ // Date Version Author Description // 2024-01-25 0.2 A.Gonzalez Generic TLB for CVA6 with Hypervisor support // =========================================================================== // -module cva6_tlb import ariane_pkg::*; #( - parameter type pte_cva6_t = logic, - parameter type tlb_update_cva6_t = logic, - parameter int unsigned TLB_ENTRIES = 4, - parameter int unsigned HYP_EXT = 0, - parameter int unsigned ASID_WIDTH [HYP_EXT:0] = {1}, //[vmid_width,asid_width] - 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 -// 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, -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] -output logic [PT_LEVELS-2:0] lu_is_page_o, -output logic lu_hit_o +module cva6_tlb + import ariane_pkg::*; +#( + parameter type pte_cva6_t = logic, + parameter type tlb_update_cva6_t = logic, + parameter int unsigned TLB_ENTRIES = 4, + parameter int unsigned HYP_EXT = 0, + parameter int unsigned ASID_WIDTH[HYP_EXT:0] = {1}, //[vmid_width,asid_width] + 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 + // 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, + 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] + output logic [PT_LEVELS-2:0] lu_is_page_o, + output logic lu_hit_o ); // computes the paddr based on the page size, ppn and offset -function automatic logic [(riscv::GPLEN-1):0] make_gpaddr( - input logic s_st_enbl, input logic is_1G, input logic is_2M, - input logic [(riscv::VLEN-1):0] vaddr, input riscv::pte_t pte); - logic [(riscv::GPLEN-1):0] gpaddr; - if (s_st_enbl) begin + function automatic logic [(riscv::GPLEN-1):0] make_gpaddr( + input logic s_st_enbl, input logic is_1G, input logic is_2M, + input logic [(riscv::VLEN-1):0] vaddr, input riscv::pte_t pte); + logic [(riscv::GPLEN-1):0] gpaddr; + if (s_st_enbl) begin gpaddr = {pte.ppn[(riscv::GPPNW-1):0], vaddr[11:0]}; // Giga page if (is_1G) gpaddr[29:12] = vaddr[29:12]; // Mega page if (is_2M) gpaddr[20:12] = vaddr[20:12]; - end else begin + end else begin gpaddr = vaddr[(riscv::GPLEN-1):0]; - end -return gpaddr; -endfunction : make_gpaddr - -// computes the final gppn based on the guest physical address -function automatic logic [(riscv::GPPNW-1):0] make_gppn( - input logic s_st_enbl, input logic is_1G, input logic is_2M, - input logic [28:0] vpn, input riscv::pte_t pte); - logic [(riscv::GPPNW-1):0] gppn; - if (s_st_enbl) begin + end + return gpaddr; + endfunction : make_gpaddr + + // computes the final gppn based on the guest physical address + function automatic logic [(riscv::GPPNW-1):0] make_gppn(input logic s_st_enbl, input logic is_1G, + input logic is_2M, input logic [28:0] vpn, + input riscv::pte_t pte); + logic [(riscv::GPPNW-1):0] gppn; + if (s_st_enbl) begin gppn = pte.ppn[(riscv::GPPNW-1):0]; - if(is_2M) - gppn[8:0] = vpn[8:0]; - if(is_1G) - gppn[17:0] = vpn[17:0]; - end else begin + if (is_2M) gppn[8:0] = vpn[8:0]; + if (is_1G) gppn[17:0] = vpn[17:0]; + end else begin gppn = vpn; - end -return gppn; -endfunction : make_gppn - -// SV39 defines three levels of page tables -struct packed { -logic [HYP_EXT:0][ASID_WIDTH[0]-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 valid; -} [TLB_ENTRIES-1:0] tags_q, tags_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][PT_LEVELS-1:0] level_match; -logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vaddr_vpn_match; -logic [TLB_ENTRIES-1:0][HYP_EXT: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][HYP_EXT:0] match_asid; -logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; -logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vpage_match; -logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; -logic [TLB_ENTRIES-1:0] match_stage,tag_valid; -pte_cva6_t g_content; -logic [TLB_ENTRIES-1:0] [(riscv::GPPNW-1):0] gppn; -logic [HYP_EXT*2:0] v_st_enbl; - -assign v_st_enbl = (HYP_EXT==1) ? v_st_enbl_i : '1; -//------------- -// Translation -//------------- - -genvar i,x,z,w; -generate -for (i=0; i < TLB_ENTRIES; i++) begin - 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==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 - &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl[HYP_EXT:0])): + end + return gppn; + endfunction : make_gppn + + // SV39 defines three levels of page tables + struct packed { + logic [HYP_EXT:0][ASID_WIDTH[0]-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 valid; + } [TLB_ENTRIES-1:0] + tags_q, tags_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][PT_LEVELS-1:0] level_match; + logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vaddr_vpn_match; + logic [TLB_ENTRIES-1:0][HYP_EXT: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][HYP_EXT:0] match_asid; + logic [TLB_ENTRIES-1:0][PT_LEVELS-1:0] page_match; + logic [TLB_ENTRIES-1:0][HYP_EXT:0][PT_LEVELS-1:0] vpage_match; + logic [TLB_ENTRIES-1:0][PT_LEVELS-2:0] is_page_o; + logic [TLB_ENTRIES-1:0] match_stage, tag_valid; + pte_cva6_t g_content; + logic [TLB_ENTRIES-1:0][(riscv::GPPNW-1):0] gppn; + logic [ HYP_EXT*2:0] v_st_enbl; + + assign v_st_enbl = (HYP_EXT == 1) ? v_st_enbl_i : '1; + //------------- + // Translation + //------------- + + genvar i, x, z, w; + generate + for (i = 0; i < TLB_ENTRIES; i++) begin + 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==0 || x==(PT_LEVELS-1)) ? // PAGE_MATCH CONTAINS THE MATCH INFORMATION FOR EACH TAG OF is_1G and is_2M in sv39x4. HIGHER LEVEL (Giga page), THEN THERE IS THE Mega page AND AT THE LOWER LEVEL IS ALWAYS 1 + &(tags_q[i].is_page[PT_LEVELS-1-x] | (~v_st_enbl[HYP_EXT:0])): ((&v_st_enbl[HYP_EXT:0]) ? ((tags_q[i].is_page[PT_LEVELS-1-x][0] && (tags_q[i].is_page[PT_LEVELS-2-x][HYP_EXT] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT])) || (tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && (tags_q[i].is_page[PT_LEVELS-2-x][0] || tags_q[i].is_page[PT_LEVELS-1-x][0]))): tags_q[i].is_page[PT_LEVELS-1-x][0] && v_st_enbl[0] || tags_q[i].is_page[PT_LEVELS-1-x][HYP_EXT] && v_st_enbl[HYP_EXT])); - //identify if vpn matches at all PT levels for all TLB entries - assign vpn_match[i][x] = (HYP_EXT==1 && x==(PT_LEVELS-1) && ~v_st_enbl[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[12+VPN_LEN-1: 12+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 vpage_match for all TLB Entries and vaddr_level match (if there is a hit at each PT level for all TLB entries on the vaddr) - for(z=0;z 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 + // 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 + 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 automatic logic en; automatic int unsigned idx_base, shift, new_index; 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 + 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 -end -// sequential process -always_ff @(posedge clk_i or negedge rst_ni) begin - if(~rst_ni) begin + // 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 + end else begin tags_q <= tags_n; content_q <= content_n; plru_tree_q <= plru_tree_n; + end end -end -//-------------- -// Sanity checks -//-------------- + //-------------- + // Sanity checks + //-------------- -//pragma translate_off + //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[0] >= 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 + 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[0] >= 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 + //pragma translate_on endmodule

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 AAOHXW literal 22459 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 diff --git a/docs/04_cv32a6_design/images/ptw_pte_1.png b/docs/04_cv32a6_design/images/ptw_pte_1.png index 7be987971dcd92cd1a837cd5a9d32acd686a206b..78dc6cabd6775485a1f98d21ad85cf3a460d3196 100644 GIT binary patch literal 21586 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 literal 13635 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