Add shared TLB in 32 bits version (#1108)

This commit is contained in:
sébastien jacq 2023-03-16 10:16:09 +01:00 committed by GitHub
parent 05846e7156
commit 2c61865b18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 531 additions and 119 deletions

View file

@ -168,5 +168,6 @@ ${CVA6_REPO_DIR}/core/mmu_sv39/tlb.sv
${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
// end of manifest

View file

@ -44,8 +44,8 @@ package cva6_config_pkg;
localparam CVA6ConfigNrLoadPipeRegs = 1;
localparam CVA6ConfigNrStorePipeRegs = 0;
localparam CVA6ConfigInstrTlbEntries = 16;
localparam CVA6ConfigDataTlbEntries = 16;
localparam CVA6ConfigInstrTlbEntries = 2;
localparam CVA6ConfigDataTlbEntries = 2;
localparam CVA6ConfigRASDepth = 0;
localparam CVA6ConfigBTBEntries = 0;

View file

@ -44,8 +44,8 @@ package cva6_config_pkg;
localparam CVA6ConfigNrLoadPipeRegs = 1;
localparam CVA6ConfigNrStorePipeRegs = 0;
localparam CVA6ConfigInstrTlbEntries = 16;
localparam CVA6ConfigDataTlbEntries = 16;
localparam CVA6ConfigInstrTlbEntries = 2;
localparam CVA6ConfigDataTlbEntries = 2;
localparam CVA6ConfigRASDepth = 2;
localparam CVA6ConfigBTBEntries = 32;

View file

@ -44,8 +44,8 @@ package cva6_config_pkg;
localparam CVA6ConfigNrLoadPipeRegs = 1;
localparam CVA6ConfigNrStorePipeRegs = 0;
localparam CVA6ConfigInstrTlbEntries = 16;
localparam CVA6ConfigDataTlbEntries = 16;
localparam CVA6ConfigInstrTlbEntries = 2;
localparam CVA6ConfigDataTlbEntries = 2;
localparam CVA6ConfigRASDepth = 2;
localparam CVA6ConfigBTBEntries = 32;

View file

@ -44,8 +44,8 @@ package cva6_config_pkg;
localparam CVA6ConfigNrLoadPipeRegs = 1;
localparam CVA6ConfigNrStorePipeRegs = 0;
localparam CVA6ConfigInstrTlbEntries = 16;
localparam CVA6ConfigDataTlbEntries = 16;
localparam CVA6ConfigInstrTlbEntries = 2;
localparam CVA6ConfigDataTlbEntries = 2;
localparam CVA6ConfigRASDepth = 2;
localparam CVA6ConfigBTBEntries = 32;

View file

@ -28,8 +28,8 @@
module cva6_mmu_sv32 import ariane_pkg::*; #(
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 ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
) (
@ -87,7 +87,7 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
logic [riscv::PLEN-1:0] ptw_bad_paddr; // PTW PMP exception bad physical addr
logic [riscv::VLEN-1:0] update_vaddr;
tlb_update_sv32_t update_ptw_itlb, update_ptw_dtlb;
tlb_update_sv32_t update_itlb, update_dtlb, update_shared_tlb;
logic itlb_lu_access;
riscv::pte_sv32_t itlb_content;
@ -99,7 +99,13 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
logic dtlb_is_4M;
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;
@ -113,7 +119,7 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.update_i ( update_ptw_itlb ),
.update_i ( update_itlb ),
.lu_access_i ( itlb_lu_access ),
.lu_asid_i ( asid_i ),
@ -127,58 +133,112 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
);
cva6_tlb_sv32 #(
.TLB_ENTRIES ( DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.TLB_ENTRIES ( DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
) 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_4M_o ( dtlb_is_4M ),
.lu_hit_o ( dtlb_lu_hit )
);
cva6_shared_tlb_sv32 #(
.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 ),
.update_i ( update_ptw_dtlb ),
.enable_translation_i ( enable_translation_i ),
.en_ld_st_translation_i ( en_ld_st_translation_i),
.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 ),
.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 ),
.lu_is_4M_o ( dtlb_is_4M ),
.lu_hit_o ( dtlb_lu_hit )
.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 #(
.ASID_WIDTH ( ASID_WIDTH ),
.ArianeCfg ( ArianeCfg )
) 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 ),
.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 ),
.lsu_is_store_i ( lsu_is_store_i ),
// PTW memory interface
.req_port_i ( req_port_i ),
.req_port_o ( req_port_o ),
.pmpcfg_i,
.pmpaddr_i,
.bad_paddr_o ( ptw_bad_paddr ),
.*
);
// 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
@ -189,8 +249,8 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// .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
@ -215,8 +275,7 @@ module cva6_mmu_sv32 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 = 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
@ -266,7 +325,7 @@ module cva6_mmu_sv32 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) || (!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, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1};//to check on wave --> not connected
end
end
@ -330,7 +389,7 @@ module cva6_mmu_sv32 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 = 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
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

View file

@ -39,39 +39,34 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
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 enable_translation_i, // CSRs indicate to enable SV32
input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
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_sv32_t itlb_update_o,
output tlb_update_sv32_t dtlb_update_o,
// to Shared TLB, update logic
output tlb_update_sv32_t shared_tlb_update_o,
output logic [riscv::VLEN-1:0] update_vaddr_o,
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,
// 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 itlb_miss_o,
output logic dtlb_miss_o,
// PMP
// 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
@ -92,7 +87,8 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
PTE_LOOKUP,
WAIT_RVALID,
PROPAGATE_ERROR,
PROPAGATE_ACCESS_ERROR
PROPAGATE_ACCESS_ERROR,
LATENCY
} state_q, state_d;
// SV32 defines two levels of page tables
@ -116,6 +112,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
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];
@ -124,20 +121,18 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
assign req_port_o.kill_req = '0;
// we are never going to write with the HPTW
assign req_port_o.data_wdata = '0;
// -----------
// TLB Update
// Shared TLB Update
// -----------
assign itlb_update_o.vpn = vaddr_q[riscv::SV-1:12];
assign dtlb_update_o.vpn = vaddr_q[riscv::SV-1:12];
assign shared_tlb_update_o.vpn = vaddr_q[riscv::SV-1:12];
// update the correct page table level
assign itlb_update_o.is_4M = (ptw_lvl_q == LVL1);
assign dtlb_update_o.is_4M = (ptw_lvl_q == LVL1);
assign shared_tlb_update_o.is_4M = (ptw_lvl_q == LVL1);
// output the correct ASID
assign itlb_update_o.asid = tlb_update_asid_q;
assign dtlb_update_o.asid = tlb_update_asid_q;
assign shared_tlb_update_o.asid = tlb_update_asid_q;
// set the global mapping bit
assign itlb_update_o.content = pte | (global_mapping_q << 5);
assign dtlb_update_o.content = pte | (global_mapping_q << 5);
assign shared_tlb_update_o.content = pte | (global_mapping_q << 5);
assign req_port_o.tag_valid = tag_valid_q;
@ -190,25 +185,23 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
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;
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;
state_d = state_q;
global_mapping_n = global_mapping_q;
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;
tlb_update_asid_n = tlb_update_asid_q;
vaddr_n = vaddr_q;
itlb_miss_o = 1'b0;
dtlb_miss_o = 1'b0;
shared_tlb_miss_o = 1'b0;
case (state_q)
@ -217,21 +210,14 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
ptw_lvl_n = LVL1;
global_mapping_n = 1'b0;
is_instr_ptw_n = 1'b0;
// if we got an ITLB miss
if (enable_translation_i & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin
ptw_pptr_n = {satp_ppn_i, itlb_vaddr_i[riscv::SV-1:22], 2'b0}; // SATP.PPN * PAGESIZE + VPN*PTESIZE = SATP.PPN * 2^(12) + VPN*4
is_instr_ptw_n = 1'b1;
// 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:22], 2'b0}; // 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 = itlb_vaddr_i;
vaddr_n = shared_tlb_vaddr_i;
state_d = WAIT_GRANT;
itlb_miss_o = 1'b1;
// we got an DTLB miss
end else if (en_ld_st_translation_i & dtlb_access_i & ~dtlb_hit_i) begin
ptw_pptr_n = {satp_ppn_i, dtlb_vaddr_i[riscv::SV-1:22], 2'b0}; // SATP.PPN * PAGESIZE + VPN*PTESIZE = SATP.PPN * 2^(12) + VPN*4
tlb_update_asid_n = asid_i;
vaddr_n = dtlb_vaddr_i;
state_d = WAIT_GRANT;
dtlb_miss_o = 1'b1;
shared_tlb_miss_o = 1'b1;
end
end
@ -264,7 +250,8 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
// Valid PTE
// -----------
else begin
state_d = IDLE;
//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
@ -279,7 +266,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
if (!pte.x || !pte.a)
state_d = PROPAGATE_ERROR;
else
itlb_update_o.valid = 1'b1;
shared_tlb_update_o.valid = 1'b1;
end else begin
// ------------
@ -291,7 +278,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
// we can directly raise an error. This doesn't put a useless
// entry into the TLB.
if (pte.a && (pte.r || (pte.x && mxr_i))) begin
dtlb_update_o.valid = 1'b1;
shared_tlb_update_o.valid = 1'b1;
end else begin
state_d = PROPAGATE_ERROR;
end
@ -299,7 +286,7 @@ module cva6_ptw_sv32 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.w || !pte.d)) begin
dtlb_update_o.valid = 1'b0;
shared_tlb_update_o.valid = 1'b0;
state_d = PROPAGATE_ERROR;
end
end
@ -308,8 +295,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
// exception.
if (ptw_lvl_q == LVL1 && pte.ppn[9:0] != '0) begin
state_d = PROPAGATE_ERROR;
dtlb_update_o.valid = 1'b0;
itlb_update_o.valid = 1'b0;
shared_tlb_update_o.valid = 1'b0;
end
// this is a pointer to the next TLB level
end else begin
@ -332,8 +318,7 @@ module cva6_ptw_sv32 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;
state_d = PROPAGATE_ACCESS_ERROR;
@ -343,17 +328,20 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
end
// Propagate error to MMU/LSU
PROPAGATE_ERROR: begin
state_d = IDLE;
state_d = LATENCY;
ptw_error_o = 1'b1;
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
WAIT_RVALID: begin
if (data_rvalid_q)
state_d = IDLE;
end
LATENCY: begin
state_d = IDLE;
end
default: begin
state_d = IDLE;
@ -372,7 +360,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
if ((state_q == PTE_LOOKUP && !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

View file

@ -0,0 +1,364 @@
// 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_sv32 import ariane_pkg::*; #(
parameter int SHARED_TLB_DEPTH = 64,
parameter int SHARED_TLB_WAYS = 2,
parameter int ASID_WIDTH = 1,
parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
) (
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_sv32_t itlb_update_o,
output tlb_update_sv32_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_sv32_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 [8:0] asid; //9 bits wide
logic [9:0] vpn1; //10 bits wide
logic [9:0] vpn0; //10 bits wide
logic is_4M;
} 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_sv32_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_sv32_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 [9:0] vpn0_d, vpn1_d, vpn0_q, vpn1_q;
riscv::pte_sv32_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;
vpn0_d = vpn0_q;
vpn1_d = vpn1_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)];
vpn0_d = itlb_vaddr_i[21:12];
vpn1_d = itlb_vaddr_i[31:22];
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)];
vpn0_d = dtlb_vaddr_i[21:12];
vpn1_d = dtlb_vaddr_i[31:22];
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) && vpn1_q == shared_tag_rd[i].vpn1) begin
if (shared_tag_rd[i].is_4M || vpn0_q == shared_tag_rd[i].vpn0) 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_4M = shared_tag_rd[i].is_4M;
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_4M = shared_tag_rd[i].is_4M;
dtlb_update_o.asid = tlb_update_asid_q;
dtlb_update_o.content = pte[i];
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;
vpn0_q <= '0;
vpn1_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;
vpn0_q <= vpn0_d;
vpn1_q <= vpn1_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.vpn1 = shared_tlb_update_i.vpn[19:10];
assign shared_tag_wr.vpn0 = shared_tlb_update_i.vpn[9:0];
assign shared_tag_wr.is_4M = shared_tlb_update_i.is_4M;
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_sv32_t'(pte_rd_data[i]);
end
endmodule
/* verilator lint_on WIDTH */