PTW: Coding style + H-Mode fixes (#2978)
Some checks are pending
bender-up-to-date / bender-up-to-date (push) Waiting to run
ci / build-riscv-tests (push) Waiting to run
ci / execute-riscv64-tests (push) Blocked by required conditions
ci / execute-riscv32-tests (push) Blocked by required conditions

Break-down of #2933 : PTW changes only:

walks on intermediate nodes during G-translation do no generate faults if R/D/X bits is not set and request is a store (fixes [BUG] Wrong permission check walking HGATP #2910)
walks on intermediate nodes during G-translation do no generate faults if X bits is not set and request is an execute
Code style:

gather requests to the TLB in a single process instead of scattered signals
add more comments

---------

Co-authored-by: JeanRochCoulon <jean-roch.coulon@thalesgroup.com>
This commit is contained in:
Nicolas Derumigny 2025-06-18 08:14:32 +02:00 committed by GitHub
parent b484f5f3ee
commit c76b568bbd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -49,9 +49,9 @@ module cva6_ptw
input logic ld_st_v_i, // load/store virtualization mode bit
input logic hlvx_inst_i, // is a HLVX load/store instruction
input logic lsu_is_store_i, // this translation was triggered by a store
input logic lsu_is_store_i, // this translation was triggered by a store
// PTW memory interface
input dcache_req_o_t req_port_i,
input dcache_req_o_t req_port_i,
output dcache_req_i_t req_port_o,
@ -74,7 +74,7 @@ module cva6_ptw
// from CSR file
input logic [CVA6Cfg.PPNW-1:0] satp_ppn_i, // ppn from satp
input logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_i, // ppn from satp
input logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_i, // ppn from vsatp
input logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_i, // ppn from hgatp
input logic mxr_i,
input logic vmxr_i,
@ -110,6 +110,7 @@ module cva6_ptw
state_q, state_d;
logic [CVA6Cfg.PtLevels-2:0] misaligned_page;
logic shared_tlb_update_valid;
logic [HYP_EXT:0][CVA6Cfg.PtLevels-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
@ -163,7 +164,6 @@ module cva6_ptw
assign gpaddr_base = {pte.ppn[CVA6Cfg.GPPNW-1:0], vaddr_q[11:0]};
assign gpaddr[CVA6Cfg.PtLevels-1] = gpaddr_base;
assign shared_tlb_update_o.vpn = CVA6Cfg.VpnLen'(vaddr_q[CVA6Cfg.SV+HYP_EXT*2-1:12]);
genvar z, w;
generate
@ -174,16 +174,16 @@ module cva6_ptw
// exception.
assign misaligned_page[z] = (ptw_lvl_q[0] == (z)) && (pte.ppn[(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-1-z)-1:0] != '0);
//record the vaddr corresponding to each level
// 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+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))] :
w==1 ? gptw_pptr_q[12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))]:
gpaddr_q[12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))];
assign vaddr_lvl[w][z] = w==0 ? vaddr_q[12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))]:
w==1 ? gptw_pptr_q[12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))]:
gpaddr_q[12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-1))-1:12+((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)*(CVA6Cfg.PtLevels-z-2))];
end
if (CVA6Cfg.RVH) begin
assign gpaddr[z][CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0]= (ptw_lvl_q[0] == z) ? vaddr_q[CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0] : gpaddr_base[CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0];
assign gpaddr[z][CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1]= (ptw_lvl_q[0] == 0) ? vaddr_q[CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1] : gpaddr_base[CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1];
assign gpaddr[z][CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0] = (ptw_lvl_q[0] == z) ? vaddr_q[CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0] : gpaddr_base[CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels):0];
assign gpaddr[z][CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1] = (ptw_lvl_q[0] == 0) ? vaddr_q[CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1] : gpaddr_base[CVA6Cfg.VpnLen:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)+1];
assign gpaddr[z][CVA6Cfg.GPLEN-1:CVA6Cfg.VpnLen+1] = gpaddr_base[CVA6Cfg.GPLEN-1:CVA6Cfg.VpnLen+1];
end
@ -192,14 +192,19 @@ module cva6_ptw
endgenerate
always_comb begin : tlb_update
shared_tlb_update_o.valid = shared_tlb_update_valid;
// update the correct page table level
for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin
for (int unsigned x = 0; x < CVA6Cfg.PtLevels - 1; x++) begin
if((enable_g_translation_i && enable_translation_i) || (en_ld_st_g_translation_i && en_ld_st_translation_i) && CVA6Cfg.RVH) begin
shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==HYP_EXT?0 : 1] == x);
if(((enable_g_translation_i && enable_translation_i) || (en_ld_st_g_translation_i && en_ld_st_translation_i)) && CVA6Cfg.RVH) begin
// VS + G-Translation
shared_tlb_update_o.is_page[x][y] = (ptw_lvl_q[y==1?0 : 1] == x);
end else if (enable_translation_i || en_ld_st_translation_i || !CVA6Cfg.RVH) begin
// non-V, S-Translation
shared_tlb_update_o.is_page[x][y] = y == 0 ? (ptw_lvl_q[0] == x) : 1'b0;
end else begin
// G-Translation
shared_tlb_update_o.is_page[x][y] = y != 0 ? (ptw_lvl_q[0] == x) : 1'b0;
end
end
@ -207,7 +212,7 @@ module cva6_ptw
// set the global mapping bit
if ((enable_g_translation_i || en_ld_st_g_translation_i) && CVA6Cfg.RVH) begin
shared_tlb_update_o.content = gpte_q | (global_mapping_q << 5);
shared_tlb_update_o.content = (gpte_q | (global_mapping_q << 5));
shared_tlb_update_o.g_content = pte;
end else begin
shared_tlb_update_o.content = (pte | (global_mapping_q << 5));
@ -217,6 +222,7 @@ module cva6_ptw
// output the correct ASIDs
shared_tlb_update_o.asid = tlb_update_asid_q;
shared_tlb_update_o.vmid = CVA6Cfg.RVH ? tlb_update_vmid_q : '0;
shared_tlb_update_o.vpn = vaddr_q[12+CVA6Cfg.VpnLen-1:12];
bad_paddr_o = ptw_access_exception_o ? ptw_pptr_q : 'b0;
if (CVA6Cfg.RVH)
@ -276,28 +282,27 @@ module cva6_ptw
// - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
always_comb begin : ptw
automatic logic [CVA6Cfg.PLEN-1:0] pptr;
// automatic logic [CVA6Cfg.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'(CVA6Cfg.PtLevels);
req_port_o.data_we = 1'b0;
ptw_error_o = 1'b0;
ptw_error_at_g_st_o = 1'b0;
ptw_err_at_g_int_st_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;
ptw_stage_d = ptw_stage_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'(CVA6Cfg.PtLevels);
req_port_o.data_we = 1'b0;
ptw_error_o = 1'b0;
ptw_error_at_g_st_o = 1'b0;
ptw_err_at_g_int_st_o = 1'b0;
ptw_access_exception_o = 1'b0;
shared_tlb_update_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;
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;
pptr = ptw_pptr_q;
tlb_update_asid_n = tlb_update_asid_q;
vaddr_n = vaddr_q;
pptr = ptw_pptr_q;
if (CVA6Cfg.RVH) begin
gpaddr_n = gpaddr_q;
@ -407,8 +412,7 @@ module cva6_ptw
// -----------
else begin
state_d = LATENCY;
// it is a valid PTE
// if pte.r = 1 or pte.x = 1 it is a valid PTE
// it is a valid PTE if pte.r = 1 or pte.x = 1
if (pte.r || pte.x) begin
if (CVA6Cfg.RVH) begin
case (ptw_stage_q)
@ -421,7 +425,7 @@ module cva6_ptw
gpaddr_n = gpaddr[ptw_lvl_q[0]];
ptw_pptr_n = {
hgatp_ppn_i[CVA6Cfg.PPNW-1:2],
gpaddr[ptw_lvl_q[0]][CVA6Cfg.SV+HYP_EXT*2-1:CVA6Cfg.SV-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)],
gpaddr[ptw_lvl_q[0]][CVA6Cfg.GPLEN-1:CVA6Cfg.SV-(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)],
(CVA6Cfg.PtLevels)'(0)
};
ptw_lvl_n[0] = '0;
@ -447,11 +451,12 @@ module cva6_ptw
// 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) begin
if ((!pte.x && (!CVA6Cfg.RVH || ptw_stage_q != G_INTERMED_STAGE)) || !pte.a
|| (CVA6Cfg.RVH && ptw_stage_q == G_INTERMED_STAGE && !pte.r)) begin
state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;
end else if (CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !enable_g_translation_i) || !CVA6Cfg.RVH)
shared_tlb_update_o.valid = 1'b1;
end else if ((CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !enable_g_translation_i)) || !CVA6Cfg.RVH)
shared_tlb_update_valid = 1'b1;
end else begin
// ------------
@ -462,33 +467,32 @@ module cva6_ptw
// 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 && !hlvx_inst_i) || (pte.x && (mxr_i || hlvx_inst_i || (ptw_stage_q == S_STAGE && vmxr_i && ld_st_v_i && CVA6Cfg.RVH))))) begin
if (CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_g_translation_i) || !CVA6Cfg.RVH)
shared_tlb_update_o.valid = 1'b1;
if (
(pte.a && ((pte.r && !hlvx_inst_i) || (pte.x && (mxr_i || hlvx_inst_i || (ptw_stage_q == S_STAGE && vmxr_i && ld_st_v_i && CVA6Cfg.RVH)))))
// 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
// g-intermediate nodes however never need write-permission
&& (!lsu_is_store_i || (pte.w && pte.d) || (ptw_stage_q == G_INTERMED_STAGE && CVA6Cfg.RVH))
) begin
if ((CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_g_translation_i)) || !CVA6Cfg.RVH)
shared_tlb_update_valid = 1'b1;
end else begin
state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) 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.w || !pte.d)) begin
shared_tlb_update_o.valid = 1'b0;
state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;
end
end
//if there is a misaligned page, propagate error
// if there is a misaligned page, propagate error
if (|misaligned_page) begin
state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;
shared_tlb_update_o.valid = 1'b0;
shared_tlb_update_valid = 1'b0;
end
// check if 63:41 are all zeros
if (CVA6Cfg.RVH) begin
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW-1+HYP_EXT]) == 1'b0)) begin
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW]) == 1'b0)) begin
state_d = PROPAGATE_ERROR;
ptw_stage_d = G_FINAL_STAGE;
end
@ -513,7 +517,6 @@ module cva6_ptw
S_STAGE: begin
if (CVA6Cfg.RVH && ((is_instr_ptw_q && enable_g_translation_i) || (!is_instr_ptw_q && en_ld_st_g_translation_i))) begin
ptw_stage_d = G_INTERMED_STAGE;
if (CVA6Cfg.RVH) gpte_d = pte;
ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1;
pptr = {pte.ppn, vaddr_lvl[0][ptw_lvl_q[0]], (CVA6Cfg.PtLevels)'(0)};
gptw_pptr_n = pptr;
@ -549,7 +552,7 @@ module cva6_ptw
// check if 63:41 are all zeros
if (CVA6Cfg.RVH) begin
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW-1+HYP_EXT]) == 1'b0)) begin
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW]) == 1'b0)) begin
state_d = PROPAGATE_ERROR;
ptw_stage_d = ptw_stage_q;
end
@ -557,9 +560,9 @@ module cva6_ptw
end
end
// Check if this access was actually allowed from a PMP perspective
// check if this access was actually allowed from a PMP perspective
if (!allow_access) begin
shared_tlb_update_o.valid = 1'b0;
shared_tlb_update_valid = 1'b0;
// we have to return the failed address in bad_addr
ptw_pptr_n = ptw_pptr_q;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;