@@ -89,6 +89,7 @@ module cva6_shared_tlb #(
8989 logic [CVA6Cfg.PtLevels+ HYP_EXT - 1 : 0 ][(CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)- 1 : 0 ] vpn;
9090 logic [CVA6Cfg.PtLevels- 2 : 0 ][HYP_EXT : 0 ] is_page;
9191 logic [HYP_EXT * 2 : 0 ] v_st_enbl; // v_i,g-stage enabled, s-stage enabled
92+ logic is_napot_64k; // Svnapot: Flag indicating a 64KiB NAPOT page
9293 } shared_tag_t ;
9394
9495 shared_tag_t shared_tag_wr;
@@ -143,6 +144,11 @@ module cva6_shared_tlb #(
143144 logic shared_tlb_hit_d;
144145 logic [CVA6Cfg.VLEN - 1 : 0 ] shared_tlb_vaddr_q, shared_tlb_vaddr_d;
145146
147+ logic [ CVA6Cfg.VpnLen- 1 : 0 ] lu_vpn;
148+ logic [SHARED_TLB_WAYS - 1 : 0 ] vpn0_napot_match;
149+ logic [SHARED_TLB_WAYS - 1 : 0 ] napot_tag_match;
150+ pte_cva6_t patched_pte;
151+
146152 logic itlb_req_d, itlb_req_q;
147153 logic dtlb_req_d, dtlb_req_q;
148154
@@ -164,39 +170,42 @@ module cva6_shared_tlb #(
164170 assign shared_tlb_vaddr_o = shared_tlb_vaddr_q;
165171 assign itlb_req_o = itlb_req_q;
166172 assign v_st_enbl = {{ v_i, g_st_enbl_i, s_st_enbl_i} , { ld_st_v_i, g_ld_st_enbl_i, s_ld_st_enbl_i}} ;
173+ assign lu_vpn = itlb_req_q ? itlb_vpn_q : dtlb_vpn_q;
167174
168- genvar i, x ;
175+ genvar i_gen, x_gen ;
169176 generate
170- for (i = 0 ; i < SHARED_TLB_WAYS ; i ++ ) begin : gen_match_tlb_ways
177+ for (i_gen = 0 ; i_gen < SHARED_TLB_WAYS ; i_gen ++ ) begin : gen_match_tlb_ways
171178 // identify page_match for all TLB Entries
172-
173- for (x = 0 ; x < CVA6Cfg.PtLevels; x++ ) begin : gen_match
174- assign page_match[i][x] = x== 0 ? 1 : ((HYP_EXT == 0 || x== (CVA6Cfg.PtLevels- 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
175- & (shared_tag_rd[i].is_page[CVA6Cfg.PtLevels- 1 - x] | (~ v_st_enbl[i_req_q][HYP_EXT : 0 ])):
179+ for (x_gen = 0 ; x_gen < CVA6Cfg.PtLevels; x_gen++ ) begin : gen_match
180+ assign page_match[i_gen][x_gen] = x_gen== 0 ? 1 : ((HYP_EXT == 0 || x_gen== (CVA6Cfg.PtLevels- 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
181+ & (shared_tag_rd[i_gen].is_page[CVA6Cfg.PtLevels- 1 - x_gen] | (~ v_st_enbl[i_req_q][HYP_EXT : 0 ])):
176182 ((& v_st_enbl[i_req_q][HYP_EXT : 0 ]) ?
177- ((shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][0 ] && (shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 2 - x ][HYP_EXT ] || shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][HYP_EXT ]))
178- || (shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][HYP_EXT ] && (shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 2 - x ][0 ] || shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][0 ]))):
179- shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][0 ] && v_st_enbl[i_req_q][0 ] || shared_tag_rd[i ].is_page[CVA6Cfg.PtLevels- 1 - x ][HYP_EXT ] && v_st_enbl[i_req_q][HYP_EXT ]));
183+ ((shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][0 ] && (shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 2 - x_gen ][HYP_EXT ] || shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][HYP_EXT ]))
184+ || (shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][HYP_EXT ] && (shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 2 - x_gen ][0 ] || shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][0 ]))):
185+ shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][0 ] && v_st_enbl[i_req_q][0 ] || shared_tag_rd[i_gen ].is_page[CVA6Cfg.PtLevels- 1 - x_gen ][HYP_EXT ] && v_st_enbl[i_req_q][HYP_EXT ]));
180186
181187 // identify if vpn matches at all PT levels for all TLB entries
182- assign vpn_match[i][x ] = (HYP_EXT == 1 && x == (CVA6Cfg.PtLevels- 1 ) && ~ v_st_enbl[i_req_q][0 ]) ? //
183- vpn_q[x ] == shared_tag_rd[i ].vpn[x ] && vpn_q[x + HYP_EXT ][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- HYP_EXT : 0 ] == shared_tag_rd[i ].vpn[x + HYP_EXT ][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- HYP_EXT : 0 ]: //
184- vpn_q[x ] == shared_tag_rd[i ].vpn[x ];
188+ assign vpn_match[i_gen][x_gen ] = (HYP_EXT == 1 && x_gen == (CVA6Cfg.PtLevels- 1 ) && ~ v_st_enbl[i_req_q][0 ]) ? //
189+ vpn_q[x_gen ] == shared_tag_rd[i_gen ].vpn[x_gen ] && vpn_q[x_gen + HYP_EXT ][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- HYP_EXT : 0 ] == shared_tag_rd[i_gen ].vpn[x_gen + HYP_EXT ][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- HYP_EXT : 0 ]: //
190+ vpn_q[x_gen ] == shared_tag_rd[i_gen ].vpn[x_gen ];
185191
186192 // identify if there is a hit at each PT level for all TLB entries
187- assign level_match[i][x ] = & vpn_match[i ][CVA6Cfg.PtLevels- 1 : x ] && page_match[i][x ];
193+ assign level_match[i_gen][x_gen ] = & vpn_match[i_gen ][CVA6Cfg.PtLevels- 1 : x_gen ] && page_match[i_gen][x_gen ];
188194
189195 end
196+ assign vpn0_napot_match[i_gen] = lu_vpn[(CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)- 1 : 4 ] == shared_tag_rd[i_gen].vpn[0 ][(CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)- 1 : 4 ];
197+ assign napot_tag_match[i_gen] = (CVA6Cfg.SvnapotEn && shared_tag_rd[i_gen].is_napot_64k) ?
198+ (vpn_match[i_gen][2 ] && vpn_match[i_gen][1 ] && vpn0_napot_match[i_gen]) : 1'b0 ;
190199 end
191200 endgenerate
192201
193- genvar w ;
202+ genvar w_gen ;
194203 generate
195- for (w = 0 ; w < CVA6Cfg.PtLevels; w ++ ) begin
196- assign vpn_d[w ] = ((| v_st_enbl[1 ][HYP_EXT : 0 ]) && itlb_access_i && ~ itlb_hit_i && ~ dtlb_access_i) ? //
197- itlb_vaddr_i[12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (w + 1 ))- 1 : 12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* w )] : //
204+ for (w_gen = 0 ; w_gen < CVA6Cfg.PtLevels; w_gen ++ ) begin
205+ assign vpn_d[w_gen ] = ((| v_st_enbl[1 ][HYP_EXT : 0 ]) && itlb_access_i && ~ itlb_hit_i && ~ dtlb_access_i) ? //
206+ itlb_vaddr_i[12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (w_gen + 1 ))- 1 : 12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* w_gen )] : //
198207 (((| v_st_enbl[0 ][HYP_EXT : 0 ]) && dtlb_access_i && ~ dtlb_hit_i) ? //
199- dtlb_vaddr_i[12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (w + 1 ))- 1 : 12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* w )] : vpn_q[w ]);
208+ dtlb_vaddr_i[12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (w_gen + 1 ))- 1 : 12 + ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* w_gen )] : vpn_q[w_gen ]);
200209 end
201210 endgenerate
202211
@@ -270,7 +279,6 @@ module cva6_shared_tlb #(
270279 match_asid = '{ default : 0 } ;
271280 match_vmid = CVA6Cfg.RVH ? '{ default : 0 } : '{ default : 1 } ;
272281
273-
274282 if (! CVA6Cfg.UseSharedTlb) begin
275283 if (shared_tlb_update_i.valid) begin
276284 shared_tlb_hit_d = 1'b1 ;
@@ -283,6 +291,7 @@ module cva6_shared_tlb #(
283291 itlb_update_o.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
284292 itlb_update_o.asid = shared_tlb_update_i.asid;
285293 itlb_update_o.vmid = shared_tlb_update_i.vmid;
294+ itlb_update_o.is_napot_64k = shared_tlb_update_i.is_napot_64k;
286295
287296 end else if (dtlb_req_q) begin
288297 dtlb_update_o.valid = 1'b1 ;
@@ -293,10 +302,10 @@ module cva6_shared_tlb #(
293302 dtlb_update_o.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
294303 dtlb_update_o.asid = shared_tlb_update_i.asid;
295304 dtlb_update_o.vmid = shared_tlb_update_i.vmid;
305+ dtlb_update_o.is_napot_64k = shared_tlb_update_i.is_napot_64k;
296306 end
297307 end
298308 end else begin
299-
300309 // number of ways
301310 for (int unsigned i = 0 ; i < SHARED_TLB_WAYS ; i++ ) begin
302311 // first level match, this may be a giga page, check the ASID flags as well
@@ -309,28 +318,35 @@ module cva6_shared_tlb #(
309318
310319 // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off
311320 match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
312-
313321 if (shared_tag_valid[i] && match_asid[i] && match_vmid[i] && match_stage[i]) begin
314- if (| level_match[i]) begin
322+ if (| level_match[i] || napot_tag_match[i] ) begin
315323 shared_tlb_hit_d = 1'b1 ;
324+ // Prepare PTE with NAPOT patching if needed
325+ patched_pte = pte[i][0 ]; // take the S‑stage PTE only
326+ if (shared_tag_rd[i].is_napot_64k && CVA6Cfg.SvnapotEn) begin
327+ // replace PPN[3:0] with vaddr[15:12] (equiv. lu_vpn[3:0])
328+ patched_pte.ppn[3 : 0 ] = lu_vpn[3 : 0 ];
329+ end
316330 if (itlb_req_q) begin
317331 itlb_update_o.valid = 1'b1 ;
318332 itlb_update_o.vpn = itlb_vpn_q;
319333 itlb_update_o.is_page = shared_tag_rd[i].is_page;
320- itlb_update_o.content = pte[i][ 0 ] ;
334+ itlb_update_o.content = patched_pte ;
321335 itlb_update_o.g_content = pte[i][HYP_EXT ];
322336 itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl;
323337 itlb_update_o.asid = tlb_update_asid_q;
324338 itlb_update_o.vmid = tlb_update_vmid_q;
339+ itlb_update_o.is_napot_64k = CVA6Cfg.SvnapotEn ? shared_tag_rd[i].is_napot_64k : 1'b0 ;
325340 end else if (dtlb_req_q) begin
326341 dtlb_update_o.valid = 1'b1 ;
327342 dtlb_update_o.vpn = dtlb_vpn_q;
328343 dtlb_update_o.is_page = shared_tag_rd[i].is_page;
329- dtlb_update_o.content = pte[i][ 0 ] ;
344+ dtlb_update_o.content = patched_pte ;
330345 dtlb_update_o.g_content = pte[i][HYP_EXT ];
331346 dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl;
332347 dtlb_update_o.asid = tlb_update_asid_q;
333348 dtlb_update_o.vmid = tlb_update_vmid_q;
349+ dtlb_update_o.is_napot_64k = CVA6Cfg.SvnapotEn ? shared_tag_rd[i].is_napot_64k : 1'b0 ;
334350 end
335351 end
336352 end
@@ -381,6 +397,21 @@ module cva6_shared_tlb #(
381397 // ------------------
382398 // Update and Flush
383399 // ------------------
400+
401+ logic [CVA6Cfg.VpnLen- 1 : 0 ] vpn_to_store;
402+ logic [$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ] vpn_index;
403+
404+ // When storing the VPN in the shared TLB, if NAPOT is used, the lower bits of the VPN are zeroed
405+ // This way, when searching for a match, we can compare the full VPN if NAPOT is not used
406+ // or only the upper bits if NAPOT is used (the lower bits are don't care)
407+ // The actual PPN is stored in the PTE, with the lower bits patched if NAPOT is used
408+ // This is done in the tag comparison logic above
409+
410+ assign vpn_to_store =
411+ (shared_tlb_update_i.is_napot_64k && CVA6Cfg.SvnapotEn)
412+ ? { shared_tlb_update_i.vpn[CVA6Cfg.VpnLen- 1 : 4 ], 4'b0 }
413+ : shared_tlb_update_i.vpn;
414+
384415 always_comb begin : update_flush
385416 shared_tag_valid_d = shared_tag_valid_q;
386417 tag_wr_en = '0 ;
@@ -391,7 +422,7 @@ module cva6_shared_tlb #(
391422 end else if (shared_tlb_update_i.valid) begin
392423 for (int unsigned i = 0 ; i < SHARED_TLB_WAYS ; i++ ) begin
393424 if (repl_way_oh_d[i]) begin
394- shared_tag_valid_d[shared_tlb_update_i.vpn[ $clog2 (CVA6Cfg.SharedTlbDepth) - 1 : 0 ] ][i] = 1'b1 ;
425+ shared_tag_valid_d[vpn_index ][i] = 1'b1 ;
395426 tag_wr_en[i] = 1'b1 ;
396427 pte_wr_en[i] = 1'b1 ;
397428 end
@@ -403,23 +434,26 @@ module cva6_shared_tlb #(
403434 assign shared_tag_wr.vmid = shared_tlb_update_i.vmid;
404435 assign shared_tag_wr.is_page = shared_tlb_update_i.is_page;
405436 assign shared_tag_wr.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
406-
407- genvar z ;
437+ assign shared_tag_wr.is_napot_64k = shared_tlb_update_i.is_napot_64k; // Svnapot: Propagate the NAPOT flag from the update packet into the tag structure to be stored
438+ genvar z_gen ;
408439 generate
409- for (z = 0 ; z < CVA6Cfg.PtLevels; z ++ ) begin : gen_shared_tag
410- assign shared_tag_wr.vpn[z ] = shared_tlb_update_i.vpn [((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (z + 1 ))- 1 : ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* z )];
440+ for (z_gen = 0 ; z_gen < CVA6Cfg.PtLevels; z_gen ++ ) begin : gen_shared_tag
441+ assign shared_tag_wr.vpn[z_gen ] = vpn_to_store [((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (z_gen + 1 ))- 1 : ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* z_gen )];
411442 end
412443 if (CVA6Cfg.RVH ) begin : gen_shared_tag_hyp
413444 // THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4
414- assign shared_tag_wr.vpn[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- 1 : 0 ] = shared_tlb_update_i.vpn [CVA6Cfg.VpnLen- 1 : CVA6Cfg.VpnLen- (CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)];
445+ assign shared_tag_wr.vpn[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)- 1 : 0 ] = vpn_to_store [CVA6Cfg.VpnLen- 1 : CVA6Cfg.VpnLen- (CVA6Cfg.VpnLen% CVA6Cfg.PtLevels)];
415446 end
416447 endgenerate
417448
418449
419- assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
420450 assign tag_wr_data = shared_tag_wr;
421451
422- assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
452+ // derive the set index from bits [4 +: clog2] of the (possibly masked) VPN
453+ assign vpn_index = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
454+ // write the new entry into that set
455+ assign tag_wr_addr = vpn_index;
456+ assign pte_wr_addr = vpn_index;
423457
424458 assign pte_wr_data[0 ] = shared_tlb_update_i.content[CVA6Cfg.XLEN - 1 : 0 ];
425459 assign pte_wr_data[1 ] = shared_tlb_update_i.g_content[CVA6Cfg.XLEN - 1 : 0 ];
0 commit comments