diff --git a/core/cva6.sv b/core/cva6.sv index 92e174e4e7..2613ca325f 100644 --- a/core/cva6.sv +++ b/core/cva6.sv @@ -20,11 +20,7 @@ module cva6 // CVA6 config parameter config_pkg::cva6_cfg_t CVA6Cfg = cva6_config_pkg::cva6_cfg, parameter bit IsRVFI = bit'(cva6_config_pkg::CVA6ConfigRvfiTrace), -`ifdef ECC_EN parameter bit EccEnable = 1'b1, -`else - parameter bit EccEnable = 1'b0, -`endif // RVFI parameter type rvfi_probes_t = struct packed { logic [TRANS_ID_BITS-1:0] issue_pointer; diff --git a/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv b/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv index 840bd173e4..6a07c75a55 100644 --- a/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv +++ b/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv @@ -77,11 +77,6 @@ package cva6_config_pkg; localparam CVA6ConfigRvfiTrace = 0; -`ifdef SPLIT - localparam RedSplitSize = `SPLIT; -`else - localparam RedSplitSize = 0; -`endif localparam config_pkg::cva6_cfg_t cva6_cfg = '{ NrCommitPorts: unsigned'(CVA6ConfigNrCommitPorts), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), diff --git a/core/mmu_sv39x4/cva6_mmu_sv39x4.sv b/core/mmu_sv39x4/cva6_mmu_sv39x4.sv index ed40cb5118..726f6ec394 100644 --- a/core/mmu_sv39x4/cva6_mmu_sv39x4.sv +++ b/core/mmu_sv39x4/cva6_mmu_sv39x4.sv @@ -137,247 +137,78 @@ module cva6_mmu_sv39x4 assign dtlb_lu_asid = (ld_st_v_i || flush_tlb_vvma_i) ? vs_asid_i : asid_i; - if (EccEnable) begin - localparam TLB_SIZE = 3 + $bits(riscv::pte_t) + $bits(riscv::pte_t) + $bits(riscv::GPLEN); - localparam LOG2_TLB = $clog2(TLB_SIZE); - localparam TLB_BUS_SIZE = 2**LOG2_TLB; - localparam BUS_SPLIT_SIZE = TLB_BUS_SIZE/(2**cva6_config_pkg::RedSplitSize); - - typedef struct packed { - logic tlb_is_2M; - logic tlb_is_1G; - logic tlb_lu_hit; - riscv::pte_t tlb_g_content; - riscv::pte_t tlb_content; - logic [riscv::GPLEN-1:0] tlb_gpaddr; - logic [TLB_BUS_SIZE-TLB_SIZE-1:0] padding; - } tlb_bus_t; - - - logic [2:0] red_itlb_is_2M; - logic [2:0] red_itlb_is_1G; - logic [2:0] red_itlb_lu_hit; - riscv::pte_t [2:0] red_itlb_g_content; - riscv::pte_t [2:0] red_itlb_content; - logic [2:0][riscv::GPLEN-1:0] red_itlb_gpaddr; - tlb_bus_t [2:0] itlb_bus; - logic [TLB_BUS_SIZE-1:0] itlb_majority_helper; - tlb_bus_t itlb_majority; - - logic [2:0] red_dtlb_is_2M; - logic [2:0] red_dtlb_is_1G; - logic [2:0] red_dtlb_lu_hit; - riscv::pte_t [2:0] red_dtlb_content; - riscv::pte_t [2:0] red_dtlb_g_content; - logic [2:0][riscv::GPLEN-1:0] red_dtlb_gpaddr; - tlb_bus_t [2:0] dtlb_bus; - logic [TLB_BUS_SIZE-1:0] dtlb_majority_helper; - tlb_bus_t dtlb_majority; - - for (genvar i=0; i<3; i++) begin: gen_redundancy_bus - assign itlb_bus[i] = '{ - padding: '0, - tlb_is_2M: red_itlb_is_2M[i], - tlb_is_1G: red_itlb_is_1G[i], - tlb_lu_hit: red_itlb_lu_hit[i], - tlb_g_content: red_itlb_g_content[i], - tlb_content: red_itlb_content[i], - tlb_gpaddr: red_itlb_gpaddr[i] - }; - - assign dtlb_bus[i] = '{ - padding: '0, - tlb_is_2M: red_dtlb_is_2M[i], - tlb_is_1G: red_dtlb_is_1G[i], - tlb_lu_hit: red_dtlb_lu_hit[i], - tlb_g_content: red_dtlb_g_content[i], - tlb_content: red_dtlb_content[i], - tlb_gpaddr: red_dtlb_gpaddr[i] - }; - end + cva6_tlb_sv39x4 #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(INSTR_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VMID_WIDTH (VMID_WIDTH), + .EccEnable (EccEnable) + ) i_itlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clear_i (clear_i), + .flush_i (flush_tlb_i), + .flush_vvma_i(flush_tlb_vvma_i), + .flush_gvma_i(flush_tlb_gvma_i), + .s_st_enbl_i (enable_translation_i), + .g_st_enbl_i (enable_g_translation_i), + .v_i (v_i), + + .update_i(update_ptw_itlb), + + .lu_access_i (itlb_lu_access), + .lu_asid_i (itlb_lu_asid), + .lu_vmid_i (vmid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vmid_to_be_flushed_i (vmid_to_be_flushed_i), + .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), + .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), + .lu_vaddr_i (icache_areq_i.fetch_vaddr), + .lu_content_o (itlb_content), + .lu_g_content_o (itlb_g_content), + .lu_gpaddr_o (itlb_gpaddr), + + .lu_is_2M_o(itlb_is_2M), + .lu_is_1G_o(itlb_is_1G), + .lu_hit_o (itlb_lu_hit) + ); - for (genvar i=0; i<3; i++) begin: gen_redundancy_tlb - cva6_tlb_sv39x4 #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VMID_WIDTH (VMID_WIDTH) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i (flush_tlb_i), - .flush_vvma_i(flush_tlb_vvma_i), - .flush_gvma_i(flush_tlb_gvma_i), - .s_st_enbl_i (enable_translation_i), - .g_st_enbl_i (enable_g_translation_i), - .v_i (v_i), - - .update_i(update_ptw_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_lu_asid), - .lu_vmid_i (vmid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vmid_to_be_flushed_i (vmid_to_be_flushed_i), - .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), - .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (red_itlb_content[i]), - .lu_g_content_o (red_itlb_g_content[i]), - .lu_gpaddr_o (red_itlb_gpaddr[i]), - - .lu_is_2M_o(red_itlb_is_2M[i]), - .lu_is_1G_o(red_itlb_is_1G[i]), - .lu_hit_o (red_itlb_lu_hit[i]) - ); - - cva6_tlb_sv39x4 #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VMID_WIDTH (VMID_WIDTH) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i (flush_tlb_i), - .flush_vvma_i(flush_tlb_vvma_i), - .flush_gvma_i(flush_tlb_gvma_i), - .s_st_enbl_i (en_ld_st_translation_i), - .g_st_enbl_i (en_ld_st_g_translation_i), - .v_i (ld_st_v_i), - - .update_i(update_ptw_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_lu_asid), - .lu_vmid_i (vmid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vmid_to_be_flushed_i (vmid_to_be_flushed_i), - .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), - .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (red_dtlb_content[i]), - .lu_g_content_o (red_dtlb_g_content[i]), - .lu_gpaddr_o (red_dtlb_gpaddr[i]), - - .lu_is_2M_o(red_dtlb_is_2M[i]), - .lu_is_1G_o(red_dtlb_is_1G[i]), - .lu_hit_o (red_dtlb_lu_hit[i]) - ); - end + cva6_tlb_sv39x4 #( + .CVA6Cfg (CVA6Cfg), + .TLB_ENTRIES(DATA_TLB_ENTRIES), + .ASID_WIDTH (ASID_WIDTH), + .VMID_WIDTH (VMID_WIDTH), + .EccEnable (EccEnable) + ) i_dtlb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clear_i (clear_i), + .flush_i (flush_tlb_i), + .flush_vvma_i(flush_tlb_vvma_i), + .flush_gvma_i(flush_tlb_gvma_i), + .s_st_enbl_i (en_ld_st_translation_i), + .g_st_enbl_i (en_ld_st_g_translation_i), + .v_i (ld_st_v_i), + + .update_i(update_ptw_dtlb), + + .lu_access_i (dtlb_lu_access), + .lu_asid_i (dtlb_lu_asid), + .lu_vmid_i (vmid_i), + .asid_to_be_flushed_i (asid_to_be_flushed_i), + .vmid_to_be_flushed_i (vmid_to_be_flushed_i), + .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), + .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), + .lu_vaddr_i (lsu_vaddr_i), + .lu_content_o (dtlb_content), + .lu_g_content_o (dtlb_g_content), + .lu_gpaddr_o (dtlb_gpaddr), + + .lu_is_2M_o(dtlb_is_2M), + .lu_is_1G_o(dtlb_is_1G), + .lu_hit_o (dtlb_lu_hit) + ); - for (genvar i=0; i<2**cva6_config_pkg::RedSplitSize; i++) begin: gen_word_voter - logic [BUS_SPLIT_SIZE-1:0] itlb_helper, dtlb_helper; - - TMR_word_voter #( - .DataWidth (BUS_SPLIT_SIZE) - ) i_TMR_word_voter_itlb ( - .a_i (itlb_bus[0][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .b_i (itlb_bus[1][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .c_i (itlb_bus[2][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .majority_o (itlb_majority_helper[BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .error_o (), - .error_cba_o () - ); - - TMR_word_voter #( - .DataWidth (BUS_SPLIT_SIZE) - ) i_TMR_word_voter_dtlb ( - .a_i (dtlb_bus[0][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .b_i (dtlb_bus[1][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .c_i (dtlb_bus[2][BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .majority_o (dtlb_majority_helper[BUS_SPLIT_SIZE*(i+1) - 1 : BUS_SPLIT_SIZE*i]), - .error_o (), - .error_cba_o () - ); - end - - assign itlb_majority = tlb_bus_t'(itlb_majority_helper); - assign itlb_content = itlb_majority.tlb_content; - assign itlb_g_content = itlb_majority.tlb_g_content; - assign itlb_gpaddr = itlb_majority.tlb_gpaddr; - assign itlb_is_2M = itlb_majority.tlb_is_2M; - assign itlb_is_1G = itlb_majority.tlb_is_1G; - assign itlb_lu_hit = itlb_majority.tlb_lu_hit; - - assign dtlb_majority = tlb_bus_t'(dtlb_majority_helper); - assign dtlb_content = dtlb_majority.tlb_content; - assign dtlb_g_content = dtlb_majority.tlb_g_content; - assign dtlb_gpaddr = dtlb_majority.tlb_gpaddr; - assign dtlb_is_2M = dtlb_majority.tlb_is_2M; - assign dtlb_is_1G = dtlb_majority.tlb_is_1G; - assign dtlb_lu_hit = dtlb_majority.tlb_lu_hit; - end else begin - cva6_tlb_sv39x4 #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(INSTR_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VMID_WIDTH (VMID_WIDTH) - ) i_itlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .clear_i (clear_i), - .flush_i (flush_tlb_i), - .flush_vvma_i(flush_tlb_vvma_i), - .flush_gvma_i(flush_tlb_gvma_i), - .s_st_enbl_i (enable_translation_i), - .g_st_enbl_i (enable_g_translation_i), - .v_i (v_i), - - .update_i(update_ptw_itlb), - - .lu_access_i (itlb_lu_access), - .lu_asid_i (itlb_lu_asid), - .lu_vmid_i (vmid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vmid_to_be_flushed_i (vmid_to_be_flushed_i), - .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), - .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), - .lu_vaddr_i (icache_areq_i.fetch_vaddr), - .lu_content_o (itlb_content), - .lu_g_content_o (itlb_g_content), - .lu_gpaddr_o (itlb_gpaddr), - - .lu_is_2M_o(itlb_is_2M), - .lu_is_1G_o(itlb_is_1G), - .lu_hit_o (itlb_lu_hit) - ); - - cva6_tlb_sv39x4 #( - .CVA6Cfg (CVA6Cfg), - .TLB_ENTRIES(DATA_TLB_ENTRIES), - .ASID_WIDTH (ASID_WIDTH), - .VMID_WIDTH (VMID_WIDTH) - ) i_dtlb ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .clear_i (clear_i), - .flush_i (flush_tlb_i), - .flush_vvma_i(flush_tlb_vvma_i), - .flush_gvma_i(flush_tlb_gvma_i), - .s_st_enbl_i (en_ld_st_translation_i), - .g_st_enbl_i (en_ld_st_g_translation_i), - .v_i (ld_st_v_i), - - .update_i(update_ptw_dtlb), - - .lu_access_i (dtlb_lu_access), - .lu_asid_i (dtlb_lu_asid), - .lu_vmid_i (vmid_i), - .asid_to_be_flushed_i (asid_to_be_flushed_i), - .vmid_to_be_flushed_i (vmid_to_be_flushed_i), - .vaddr_to_be_flushed_i (vaddr_to_be_flushed_i), - .gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i), - .lu_vaddr_i (lsu_vaddr_i), - .lu_content_o (dtlb_content), - .lu_g_content_o (dtlb_g_content), - .lu_gpaddr_o (dtlb_gpaddr), - - .lu_is_2M_o(dtlb_is_2M), - .lu_is_1G_o(dtlb_is_1G), - .lu_hit_o (dtlb_lu_hit) - ); - end cva6_ptw_sv39x4 #( .CVA6Cfg (CVA6Cfg), diff --git a/core/mmu_sv39x4/cva6_tlb_sv39x4.sv b/core/mmu_sv39x4/cva6_tlb_sv39x4.sv index f9cf01cb18..0a4dac42ba 100644 --- a/core/mmu_sv39x4/cva6_tlb_sv39x4.sv +++ b/core/mmu_sv39x4/cva6_tlb_sv39x4.sv @@ -16,6 +16,7 @@ // This module is an adaptation of the Sv39 TLB developed // by Florian Zaruba and David Schaffenrath to the Sv39x4 standard. +`include "common_cells/registers.svh" module cva6_tlb_sv39x4 import ariane_pkg::*; @@ -23,10 +24,12 @@ module cva6_tlb_sv39x4 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 VMID_WIDTH = 1 + parameter int unsigned VMID_WIDTH = 1, + parameter bit EccEnable = 1'b0 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low + input logic clear_i, input logic flush_i, // Flush normal translations signal input logic flush_vvma_i, // Flush vs stage signal input logic flush_gvma_i, // Flush g stage signal @@ -53,7 +56,7 @@ module cva6_tlb_sv39x4 ); // SV39 defines three levels of page tables - struct packed { + typedef struct packed { logic [ASID_WIDTH-1:0] asid; logic [VMID_WIDTH-1:0] vmid; logic [riscv::GPPN2:0] vpn2; @@ -66,15 +69,57 @@ module cva6_tlb_sv39x4 logic s_st_enbl; // s-stage translation logic g_st_enbl; // g-stage translation logic v; // virtualization mode - logic valid; + } partial_tags_t; + + typedef struct packed { + partial_tags_t tag; + logic valid; + } tags_t; + + localparam int unsigned PteBits = $bits(riscv::pte_t); + localparam int unsigned TagBits = $bits(partial_tags_t); + localparam int unsigned ValidBits = TLB_ENTRIES; // One valid per entry + + localparam int unsigned PteCorrBits = EccEnable ? $clog2(PteBits) + 2 : 0; + localparam int unsigned PteSize = PteBits + PteCorrBits; + + localparam int unsigned TagsCorrBits = EccEnable ? $clog2(TagBits) + 2 : 0; + localparam int unsigned TagsSize = TagBits + TagsCorrBits; + + localparam int unsigned ValidCorrBits = EccEnable ? $clog2(ValidBits) + 2 : 0; + localparam int unsigned ValidSize = ValidBits + ValidCorrBits; + + logic [ValidBits-1:0] valid_update, valid_dec; + logic [ValidSize-1:0] valid_n, valid_q; + + tags_t [TLB_ENTRIES-1:0] tags; + logic [TagBits-1:0] tags_update; + logic [TagsSize-1:0] tags_enc; + logic [TLB_ENTRIES-1:0][TagBits-1:0] tags_dec; + logic [TLB_ENTRIES-1:0][TagsSize-1:0] tags_n, tags_q; + + struct packed { + logic [PteSize-1:0] pte; + logic [PteSize-1:0] gpte; + } tlb_content_n; + + struct packed { + logic [PteSize-1:0] pte; + logic [PteSize-1:0] gpte; + } [TLB_ENTRIES-1:0] + content_q, content_n; + + struct packed { + logic [PteBits-1:0] pte; + logic [PteBits-1:0] gpte; } [TLB_ENTRIES-1:0] - tags_q, tags_n; + tlb_content_dec; struct packed { riscv::pte_t pte; riscv::pte_t gpte; } [TLB_ENTRIES-1:0] - content_q, content_n; + tlb_content_q; logic [8:0] vpn0, vpn1; logic [riscv::GPPN2:0] vpn2; @@ -86,6 +131,117 @@ module cva6_tlb_sv39x4 logic [TLB_ENTRIES-1:0] is_2M; logic [TLB_ENTRIES-1:0] match_stage; riscv::pte_t g_content; + + assign tags_update = { + update_i.asid, + update_i.vmid, + update_i.vpn[18+riscv::GPPN2:18], + update_i.vpn[17:9], + update_i.vpn[8:0], + update_i.is_s_2M, + update_i.is_s_1G, + update_i.is_g_2M, + update_i.is_g_1G, + s_st_enbl_i, + g_st_enbl_i, + v_i + }; + + if (EccEnable) begin: gen_tlb_ecc + hsiao_ecc_enc #( + .DataWidth ( PteBits ), + .ProtWidth ( PteCorrBits ) + ) i_ecc_pte_enc ( + .in ( update_i.content ), + .out ( tlb_content_n.pte) + ); + + hsiao_ecc_enc #( + .DataWidth ( PteBits ), + .ProtWidth ( PteCorrBits ) + ) i_ecc_gpte_enc ( + .in ( update_i.g_content ), + .out ( tlb_content_n.gpte) + ); + + hsiao_ecc_enc #( + .DataWidth ( TagBits ), + .ProtWidth ( TagsCorrBits ) + ) i_ecc_tag_enc ( + .in ( tags_update ), + .out ( tags_enc ) + ); + + hsiao_ecc_enc #( + .DataWidth ( ValidBits ), + .ProtWidth ( ValidCorrBits ) + ) i_ecc_valid_enc ( + .in ( valid_update ), + .out ( valid_n ) + ); + + hsiao_ecc_dec #( + .DataWidth ( ValidBits ), + .ProtWidth ( ValidCorrBits ) + ) i_ecc_valid_dec ( + .in ( valid_q ), + .out ( valid_dec ), + .syndrome_o (), + .err_o () + ); + + for (genvar i = 0; i < TLB_ENTRIES; i++) begin + hsiao_ecc_dec #( + .DataWidth ( PteBits ), + .ProtWidth ( PteCorrBits ) + ) i_ecc_pte_dec ( + .in (content_q[i].pte), + .out (tlb_content_dec[i].pte), + .syndrome_o (), + .err_o () + ); + + hsiao_ecc_dec #( + .DataWidth ( PteBits ), + .ProtWidth ( PteCorrBits ) + ) i_ecc_gpte_dec ( + .in (content_q[i].gpte), + .out (tlb_content_dec[i].gpte), + .syndrome_o (), + .err_o () + ); + + hsiao_ecc_dec #( + .DataWidth ( TagBits ), + .ProtWidth ( TagsCorrBits ) + ) i_ecc_tag_dec ( + .in ( tags_q[i] ), + .out ( tags_dec[i] ), + .syndrome_o (), + .err_o () + ); + + end + end else begin: gen_no_tlb_ecc + assign tlb_content_n.pte = update_i.content; + assign tlb_content_n.gpte = update_i.g_content; + assign tags_enc = tags_update; + assign valid_n = valid_update; + assign valid_dec = valid_q; + for (genvar i = 0; i < TLB_ENTRIES; i++) begin + assign tlb_content_dec[i].pte = content_q[i].pte; + assign tlb_content_dec[i].gpte = content_q[i].gpte; + assign tags_dec[i] = tags_q[i]; + end + end + + for (genvar i = 0; i < TLB_ENTRIES; i++) begin + assign tlb_content_q[i].pte = riscv::pte_t'(tlb_content_dec[i].pte); + assign tlb_content_q[i].gpte = riscv::pte_t'(tlb_content_dec[i].gpte); + assign tags[i].tag = partial_tags_t'(tags_dec[i]); + assign tags[i].valid = valid_dec[i]; + end + //------------- // Translation //------------- @@ -115,41 +271,41 @@ module cva6_tlb_sv39x4 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] = (((lu_asid_i == tags_q[i].asid) || content_q[i].pte.g) && s_st_enbl_i) || !s_st_enbl_i; - match_vmid[i] = (lu_vmid_i == tags_q[i].vmid && g_st_enbl_i) || !g_st_enbl_i; - is_1G[i] = is_trans_1G(s_st_enbl_i, g_st_enbl_i, tags_q[i].is_s_1G, tags_q[i].is_g_1G); + match_asid[i] = (((lu_asid_i == tags[i].tag.asid) || tlb_content_q[i].pte.g) && s_st_enbl_i) || !s_st_enbl_i; + match_vmid[i] = (lu_vmid_i == tags[i].tag.vmid && g_st_enbl_i) || !g_st_enbl_i; + is_1G[i] = is_trans_1G(s_st_enbl_i, g_st_enbl_i, tags[i].tag.is_s_1G, tags[i].tag.is_g_1G); is_2M[i] = is_trans_2M( s_st_enbl_i, g_st_enbl_i, - tags_q[i].is_s_1G, - tags_q[i].is_s_2M, - tags_q[i].is_g_1G, - tags_q[i].is_g_2M + tags[i].tag.is_s_1G, + tags[i].tag.is_s_2M, + tags[i].tag.is_g_1G, + tags[i].tag.is_g_2M ); // 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 == v_i) && (tags_q[i].g_st_enbl == g_st_enbl_i) && (tags_q[i].s_st_enbl == s_st_enbl_i); - if (tags_q[i].valid && match_asid[i] && match_vmid[i] && match_stage[i] && (vpn2 == (tags_q[i].vpn2 & mask_pn2))) begin - lu_gpaddr_o = make_gpaddr(s_st_enbl_i, tags_q[i].is_s_1G, tags_q[i].is_s_2M, lu_vaddr_i, - content_q[i].pte); + match_stage[i] = (tags[i].tag.v == v_i) && (tags[i].tag.g_st_enbl == g_st_enbl_i) && (tags[i].tag.s_st_enbl == s_st_enbl_i); + if (tags[i].valid && match_asid[i] && match_vmid[i] && match_stage[i] && (vpn2 == (tags[i].tag.vpn2 & mask_pn2))) begin + lu_gpaddr_o = make_gpaddr(s_st_enbl_i, tags[i].tag.is_s_1G, tags[i].tag.is_s_2M, lu_vaddr_i, + tlb_content_q[i].pte); if (is_1G[i]) begin lu_is_1G_o = is_1G[i]; - lu_content_o = content_q[i].pte; - lu_g_content_o = content_q[i].gpte; + lu_content_o = tlb_content_q[i].pte; + lu_g_content_o = tlb_content_q[i].gpte; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; // not a giga page hit so check further - end else if (vpn1 == tags_q[i].vpn1) begin + end else if (vpn1 == tags[i].tag.vpn1) begin // this could be a 2 mega page hit or a 4 kB hit // output accordingly - if (is_2M[i] || vpn0 == tags_q[i].vpn0) begin + if (is_2M[i] || vpn0 == tags[i].tag.vpn0) begin lu_is_2M_o = is_2M[i]; // Compute G-Stage PPN based on the gpaddr - g_content = content_q[i].gpte; - if (tags_q[i].is_g_2M) g_content.ppn[8:0] = lu_gpaddr_o[20:12]; - if (tags_q[i].is_g_1G) g_content.ppn[17:0] = lu_gpaddr_o[29:12]; + g_content = tlb_content_q[i].gpte; + if (tags[i].tag.is_g_2M) g_content.ppn[8:0] = lu_gpaddr_o[20:12]; + if (tags[i].tag.is_g_1G) g_content.ppn[17:0] = lu_gpaddr_o[29:12]; // Output G-stage and S-stage content lu_g_content_o = g_content; - lu_content_o = content_q[i].pte; + lu_content_o = tlb_content_q[i].pte; lu_hit_o = 1'b1; lu_hit[i] = 1'b1; end @@ -182,95 +338,83 @@ module cva6_tlb_sv39x4 // Update and Flush // ------------------ always_comb begin : update_flush - tags_n = tags_q; + tags_n = tags_q; content_n = content_q; + valid_update = valid_dec; for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin - vaddr_vpn0_match[i] = (vaddr_to_be_flushed_i[20:12] == tags_q[i].vpn0); - vaddr_vpn1_match[i] = (vaddr_to_be_flushed_i[29:21] == tags_q[i].vpn1); - vaddr_vpn2_match[i] = (vaddr_to_be_flushed_i[30+riscv::VPN2:30] == tags_q[i].vpn2[riscv::VPN2:0]); + vaddr_vpn0_match[i] = (vaddr_to_be_flushed_i[20:12] == tags[i].tag.vpn0); + vaddr_vpn1_match[i] = (vaddr_to_be_flushed_i[29:21] == tags[i].tag.vpn1); + vaddr_vpn2_match[i] = (vaddr_to_be_flushed_i[30+riscv::VPN2:30] == tags[i].tag.vpn2[riscv::VPN2:0]); gppn[i] = make_gppn( - tags_q[i].s_st_enbl, - tags_q[i].is_s_1G, - tags_q[i].is_s_2M, + tags[i].tag.s_st_enbl, + tags[i].tag.is_s_1G, + tags[i].tag.is_s_2M, { - tags_q[i].vpn2, tags_q[i].vpn1, tags_q[i].vpn0 + tags[i].tag.vpn2, tags[i].tag.vpn1, tags[i].tag.vpn0 }, - content_q[i].pte + tlb_content_q[i].pte ); gpaddr_gppn0_match[i] = (gpaddr_to_be_flushed_i[20:12] == gppn[i][8:0]); gpaddr_gppn1_match[i] = (gpaddr_to_be_flushed_i[29:21] == gppn[i][17:9]); gpaddr_gppn2_match[i] = (gpaddr_to_be_flushed_i[30+riscv::GPPN2:30] == gppn[i][18+riscv::GPPN2:18]); if (flush_i) begin - if (!tags_q[i].v) begin + if (!tags[i].tag.v) 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 && vaddr_to_be_flushed_is0) valid_update[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 && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M) ) && (~vaddr_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if (asid_to_be_flushed_is0 && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags[i].tag.is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags[i].tag.is_s_2M) ) && (~vaddr_to_be_flushed_is0)) + valid_update[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].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M)) && (asid_to_be_flushed_i == tags_q[i].asid) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if ((!tlb_content_q[i].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags[i].tag.is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags[i].tag.is_s_2M)) && (asid_to_be_flushed_i == tags[i].tag.asid) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + valid_update[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].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if ((!tlb_content_q[i].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags[i].tag.asid) && (!asid_to_be_flushed_is0)) + valid_update[i] = 1'b0; end end else if (flush_vvma_i) begin - if (tags_q[i].v && tags_q[i].s_st_enbl) begin + if (tags[i].tag.v && tags[i].tag.s_st_enbl) 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 && vaddr_to_be_flushed_is0 && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) - tags_n[i].valid = 1'b0; + if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0 && ((tags[i].tag.g_st_enbl && lu_vmid_i == tags[i].tag.vmid) || !tags[i].tag.g_st_enbl)) + valid_update[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 && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M) ) && (~vaddr_to_be_flushed_is0) && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) - tags_n[i].valid = 1'b0; + else if (asid_to_be_flushed_is0 && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags[i].tag.is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags[i].tag.is_s_2M) ) && (~vaddr_to_be_flushed_is0) && ((tags[i].tag.g_st_enbl && lu_vmid_i == tags[i].tag.vmid) || !tags[i].tag.g_st_enbl)) + valid_update[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].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M)) && (asid_to_be_flushed_i == tags_q[i].asid && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if ((!tlb_content_q[i].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags[i].tag.is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags[i].tag.is_s_2M)) && (asid_to_be_flushed_i == tags[i].tag.asid && ((tags[i].tag.g_st_enbl && lu_vmid_i == tags[i].tag.vmid) || !tags[i].tag.g_st_enbl)) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0)) + valid_update[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].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) && (!asid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if ((!tlb_content_q[i].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags[i].tag.asid && ((tags[i].tag.g_st_enbl && lu_vmid_i == tags[i].tag.vmid) || !tags[i].tag.g_st_enbl)) && (!asid_to_be_flushed_is0)) + valid_update[i] = 1'b0; end end else if (flush_gvma_i) begin - if (tags_q[i].g_st_enbl) begin + if (tags[i].tag.g_st_enbl) begin // invalidate logic // flush everything if vmid is 0 and addr is 0 ("HFENCE.GVMA x0 x0" case) - if (vmid_to_be_flushed_is0 && gpaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0; + if (vmid_to_be_flushed_is0 && gpaddr_to_be_flushed_is0) valid_update[i] = 1'b0; // flush gpaddr in all addressing space ("HFENCE.GVMA gpaddr x0" case), it should happen only for leaf pages - else if (vmid_to_be_flushed_is0 && ((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags_q[i].is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags_q[i].is_g_2M) ) && (~gpaddr_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if (vmid_to_be_flushed_is0 && ((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags[i].tag.is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags[i].tag.is_g_2M) ) && (~gpaddr_to_be_flushed_is0)) + valid_update[i] = 1'b0; // the entry vmid and gpaddr both matches with the entry to be flushed ("HFENCE.GVMA gpaddr vmid" case) - else if (((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags_q[i].is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags_q[i].is_g_2M)) && (vmid_to_be_flushed_i == tags_q[i].vmid) && (~gpaddr_to_be_flushed_is0) && (~vmid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if (((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags[i].tag.is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags[i].tag.is_g_2M)) && (vmid_to_be_flushed_i == tags[i].tag.vmid) && (~gpaddr_to_be_flushed_is0) && (~vmid_to_be_flushed_is0)) + valid_update[i] = 1'b0; // the entry is flushed if the vmid matches and gpaddr is 0. ("HFENCE.GVMA 0 vmid" case) - else if ((gpaddr_to_be_flushed_is0) && (vmid_to_be_flushed_i == tags_q[i].vmid) && (!vmid_to_be_flushed_is0)) - tags_n[i].valid = 1'b0; + else if ((gpaddr_to_be_flushed_is0) && (vmid_to_be_flushed_i == tags[i].tag.vmid) && (!vmid_to_be_flushed_is0)) + valid_update[i] = 1'b0; end // normal replacement end else if (update_i.valid & replace_en[i]) begin // update tag array - tags_n[i] = '{ - asid: update_i.asid, - vmid: update_i.vmid, - vpn2: update_i.vpn[18+riscv::GPPN2:18], - vpn1: update_i.vpn[17:9], - vpn0: update_i.vpn[8:0], - s_st_enbl: s_st_enbl_i, - g_st_enbl: g_st_enbl_i, - v: v_i, - is_s_1G: update_i.is_s_1G, - is_s_2M: update_i.is_s_2M, - is_g_1G: update_i.is_g_1G, - is_g_2M: update_i.is_g_2M, - valid: 1'b1 - }; + tags_n[i] = tags_enc; + valid_update[i] = 1'b1; // and content as well - content_n[i].pte = update_i.content; - content_n[i].gpte = update_i.g_content; + content_n[i].pte = tlb_content_n.pte; + content_n[i].gpte = tlb_content_n.gpte; end end end @@ -357,17 +501,11 @@ module cva6_tlb_sv39x4 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 + `FFARNC(tags_q, tags_n, clear_i, '{default: 0}, clk_i, rst_ni) + `FFARNC(valid_q, valid_n, clear_i, '{default: 0}, clk_i, rst_ni) + `FFARNC(content_q, content_n, clear_i, '{default: 0}, clk_i, rst_ni) + `FFARNC(plru_tree_q, plru_tree_n, clear_i, '{default: 0}, clk_i, rst_ni) + //-------------- // Sanity checks //--------------