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

@ -74,7 +74,7 @@ module cva6_ptw
// from CSR file // from CSR file
input logic [CVA6Cfg.PPNW-1:0] satp_ppn_i, // ppn from satp 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 [CVA6Cfg.PPNW-1:0] hgatp_ppn_i, // ppn from hgatp
input logic mxr_i, input logic mxr_i,
input logic vmxr_i, input logic vmxr_i,
@ -110,6 +110,7 @@ module cva6_ptw
state_q, state_d; state_q, state_d;
logic [CVA6Cfg.PtLevels-2:0] misaligned_page; 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; 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 // 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_base = {pte.ppn[CVA6Cfg.GPPNW-1:0], vaddr_q[11:0]};
assign gpaddr[CVA6Cfg.PtLevels-1] = gpaddr_base; 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; genvar z, w;
generate generate
@ -192,14 +192,19 @@ module cva6_ptw
endgenerate endgenerate
always_comb begin : tlb_update always_comb begin : tlb_update
shared_tlb_update_o.valid = shared_tlb_update_valid;
// update the correct page table level // update the correct page table level
for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin for (int unsigned y = 0; y < HYP_EXT + 1; y++) begin
for (int unsigned x = 0; x < CVA6Cfg.PtLevels - 1; x++) 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 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); // 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 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; shared_tlb_update_o.is_page[x][y] = y == 0 ? (ptw_lvl_q[0] == x) : 1'b0;
end else begin end else begin
// G-Translation
shared_tlb_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 end
end end
@ -207,7 +212,7 @@ module cva6_ptw
// set the global mapping bit // set the global mapping bit
if ((enable_g_translation_i || en_ld_st_g_translation_i) && CVA6Cfg.RVH) begin 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; shared_tlb_update_o.g_content = pte;
end else begin end else begin
shared_tlb_update_o.content = (pte | (global_mapping_q << 5)); shared_tlb_update_o.content = (pte | (global_mapping_q << 5));
@ -217,6 +222,7 @@ module cva6_ptw
// output the correct ASIDs // output the correct ASIDs
shared_tlb_update_o.asid = tlb_update_asid_q; 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.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; bad_paddr_o = ptw_access_exception_o ? ptw_pptr_q : 'b0;
if (CVA6Cfg.RVH) if (CVA6Cfg.RVH)
@ -276,7 +282,6 @@ module cva6_ptw
// - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i]. // - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
always_comb begin : ptw always_comb begin : ptw
automatic logic [CVA6Cfg.PLEN-1:0] pptr; automatic logic [CVA6Cfg.PLEN-1:0] pptr;
// automatic logic [CVA6Cfg.GPLEN-1:0] gpaddr;
// default assignments // default assignments
// PTW memory interface // PTW memory interface
tag_valid_n = 1'b0; tag_valid_n = 1'b0;
@ -287,7 +292,7 @@ module cva6_ptw
ptw_error_at_g_st_o = 1'b0; ptw_error_at_g_st_o = 1'b0;
ptw_err_at_g_int_st_o = 1'b0; ptw_err_at_g_int_st_o = 1'b0;
ptw_access_exception_o = 1'b0; ptw_access_exception_o = 1'b0;
shared_tlb_update_o.valid = 1'b0; shared_tlb_update_valid = 1'b0;
is_instr_ptw_n = is_instr_ptw_q; is_instr_ptw_n = is_instr_ptw_q;
ptw_lvl_n = ptw_lvl_q; ptw_lvl_n = ptw_lvl_q;
ptw_pptr_n = ptw_pptr_q; ptw_pptr_n = ptw_pptr_q;
@ -407,8 +412,7 @@ module cva6_ptw
// ----------- // -----------
else begin else begin
state_d = LATENCY; state_d = LATENCY;
// it is a valid PTE // it is a valid PTE if pte.r = 1 or pte.x = 1
// if pte.r = 1 or pte.x = 1 it is a valid PTE
if (pte.r || pte.x) begin if (pte.r || pte.x) begin
if (CVA6Cfg.RVH) begin if (CVA6Cfg.RVH) begin
case (ptw_stage_q) case (ptw_stage_q)
@ -421,7 +425,7 @@ module cva6_ptw
gpaddr_n = gpaddr[ptw_lvl_q[0]]; gpaddr_n = gpaddr[ptw_lvl_q[0]];
ptw_pptr_n = { ptw_pptr_n = {
hgatp_ppn_i[CVA6Cfg.PPNW-1:2], 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) (CVA6Cfg.PtLevels)'(0)
}; };
ptw_lvl_n[0] = '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 // 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 // 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. // 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; state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q; 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) end else if ((CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !enable_g_translation_i)) || !CVA6Cfg.RVH)
shared_tlb_update_o.valid = 1'b1; shared_tlb_update_valid = 1'b1;
end else begin end else begin
// ------------ // ------------
@ -462,18 +467,17 @@ module cva6_ptw
// If page is not readable (there are no write-only pages) // If page is not readable (there are no write-only pages)
// we can directly raise an error. This doesn't put a useless // we can directly raise an error. This doesn't put a useless
// entry into the TLB. // 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 (
if (CVA6Cfg.RVH && ((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_g_translation_i) || !CVA6Cfg.RVH) (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)))))
shared_tlb_update_o.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 // Request is a store: perform some additional checks
// If the request was a store and the page is not write-able, raise an error // 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 // the same applies if the dirty flag is not set
if (lsu_is_store_i && (!pte.w || !pte.d)) begin // g-intermediate nodes however never need write-permission
shared_tlb_update_o.valid = 1'b0; && (!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; state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q; if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;
end end
@ -483,12 +487,12 @@ module cva6_ptw
if (|misaligned_page) begin if (|misaligned_page) begin
state_d = PROPAGATE_ERROR; state_d = PROPAGATE_ERROR;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q; if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;
shared_tlb_update_o.valid = 1'b0; shared_tlb_update_valid = 1'b0;
end end
// check if 63:41 are all zeros // check if 63:41 are all zeros
if (CVA6Cfg.RVH) begin 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; state_d = PROPAGATE_ERROR;
ptw_stage_d = G_FINAL_STAGE; ptw_stage_d = G_FINAL_STAGE;
end end
@ -513,7 +517,6 @@ module cva6_ptw
S_STAGE: begin 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 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; ptw_stage_d = G_INTERMED_STAGE;
if (CVA6Cfg.RVH) gpte_d = pte;
ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1; ptw_lvl_n[HYP_EXT] = ptw_lvl_q[0] + 1;
pptr = {pte.ppn, vaddr_lvl[0][ptw_lvl_q[0]], (CVA6Cfg.PtLevels)'(0)}; pptr = {pte.ppn, vaddr_lvl[0][ptw_lvl_q[0]], (CVA6Cfg.PtLevels)'(0)};
gptw_pptr_n = pptr; gptw_pptr_n = pptr;
@ -549,7 +552,7 @@ module cva6_ptw
// check if 63:41 are all zeros // check if 63:41 are all zeros
if (CVA6Cfg.RVH) begin 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; state_d = PROPAGATE_ERROR;
ptw_stage_d = ptw_stage_q; ptw_stage_d = ptw_stage_q;
end end
@ -557,9 +560,9 @@ module cva6_ptw
end end
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 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 // we have to return the failed address in bad_addr
ptw_pptr_n = ptw_pptr_q; ptw_pptr_n = ptw_pptr_q;
if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q; if (CVA6Cfg.RVH) ptw_stage_d = ptw_stage_q;