@@ -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
@@ -271,7 +280,6 @@ module cva6_shared_tlb #(
271280 match_asid = '{ default : 0 } ;
272281 match_vmid = CVA6Cfg.RVH ? '{ default : 0 } : '{ default : 1 } ;
273282
274-
275283 if (! CVA6Cfg.UseSharedTlb) begin
276284 if (shared_tlb_update_i.valid) begin
277285 shared_tlb_hit_d = 1'b1 ;
@@ -284,6 +292,7 @@ module cva6_shared_tlb #(
284292 itlb_update_o.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
285293 itlb_update_o.asid = shared_tlb_update_i.asid;
286294 itlb_update_o.vmid = shared_tlb_update_i.vmid;
295+ itlb_update_o.is_napot_64k = shared_tlb_update_i.is_napot_64k;
287296
288297 end else if (dtlb_req_q) begin
289298 dtlb_update_o.valid = 1'b1 ;
@@ -294,10 +303,10 @@ module cva6_shared_tlb #(
294303 dtlb_update_o.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
295304 dtlb_update_o.asid = shared_tlb_update_i.asid;
296305 dtlb_update_o.vmid = shared_tlb_update_i.vmid;
306+ dtlb_update_o.is_napot_64k = shared_tlb_update_i.is_napot_64k;
297307 end
298308 end
299309 end else begin
300-
301310 // number of ways
302311 for (int unsigned i = 0 ; i < SHARED_TLB_WAYS ; i++ ) begin
303312 // first level match, this may be a giga page, check the ASID flags as well
@@ -310,28 +319,35 @@ module cva6_shared_tlb #(
310319
311320 // check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off
312321 match_stage[i] = shared_tag_rd[i].v_st_enbl == v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
313-
314322 if (shared_tag_valid[i] && match_asid[i] && match_vmid[i] && match_stage[i]) begin
315- if (| level_match[i]) begin
323+ if (| level_match[i] || napot_tag_match[i] ) begin
316324 shared_tlb_hit_d = 1'b1 ;
325+ // Prepare PTE with NAPOT patching if needed
326+ patched_pte = pte[i][0 ]; // take the S‑stage PTE only
327+ if (shared_tag_rd[i].is_napot_64k && CVA6Cfg.SvnapotEn) begin
328+ // replace PPN[3:0] with vaddr[15:12] (equiv. lu_vpn[3:0])
329+ patched_pte.ppn[3 : 0 ] = lu_vpn[3 : 0 ];
330+ end
317331 if (itlb_req_q) begin
318332 itlb_update_o.valid = 1'b1 ;
319333 itlb_update_o.vpn = itlb_vpn_q;
320334 itlb_update_o.is_page = shared_tag_rd[i].is_page;
321- itlb_update_o.content = pte[i][ 0 ] ;
335+ itlb_update_o.content = patched_pte ;
322336 itlb_update_o.g_content = pte[i][HYP_EXT ];
323337 itlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl;
324338 itlb_update_o.asid = tlb_update_asid_q;
325339 itlb_update_o.vmid = tlb_update_vmid_q;
340+ itlb_update_o.is_napot_64k = CVA6Cfg.SvnapotEn ? shared_tag_rd[i].is_napot_64k : 1'b0 ;
326341 end else if (dtlb_req_q) begin
327342 dtlb_update_o.valid = 1'b1 ;
328343 dtlb_update_o.vpn = dtlb_vpn_q;
329344 dtlb_update_o.is_page = shared_tag_rd[i].is_page;
330- dtlb_update_o.content = pte[i][ 0 ] ;
345+ dtlb_update_o.content = patched_pte ;
331346 dtlb_update_o.g_content = pte[i][HYP_EXT ];
332347 dtlb_update_o.v_st_enbl = shared_tag_rd[i].v_st_enbl;
333348 dtlb_update_o.asid = tlb_update_asid_q;
334349 dtlb_update_o.vmid = tlb_update_vmid_q;
350+ dtlb_update_o.is_napot_64k = CVA6Cfg.SvnapotEn ? shared_tag_rd[i].is_napot_64k : 1'b0 ;
335351 end
336352 end
337353 end
@@ -382,6 +398,21 @@ module cva6_shared_tlb #(
382398 // ------------------
383399 // Update and Flush
384400 // ------------------
401+
402+ logic [CVA6Cfg.VpnLen- 1 : 0 ] vpn_to_store;
403+ logic [$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ] vpn_index;
404+
405+ // When storing the VPN in the shared TLB, if NAPOT is used, the lower bits of the VPN are zeroed
406+ // This way, when searching for a match, we can compare the full VPN if NAPOT is not used
407+ // or only the upper bits if NAPOT is used (the lower bits are don't care)
408+ // The actual PPN is stored in the PTE, with the lower bits patched if NAPOT is used
409+ // This is done in the tag comparison logic above
410+
411+ assign vpn_to_store =
412+ (shared_tlb_update_i.is_napot_64k && CVA6Cfg.SvnapotEn)
413+ ? { shared_tlb_update_i.vpn[CVA6Cfg.VpnLen- 1 : 4 ], 4'b0 }
414+ : shared_tlb_update_i.vpn;
415+
385416 always_comb begin : update_flush
386417 shared_tag_valid_d = shared_tag_valid_q;
387418 tag_wr_en = '0 ;
@@ -392,7 +423,7 @@ module cva6_shared_tlb #(
392423 end else if (shared_tlb_update_i.valid) begin
393424 for (int unsigned i = 0 ; i < SHARED_TLB_WAYS ; i++ ) begin
394425 if (repl_way_oh_d[i]) begin
395- shared_tag_valid_d[shared_tlb_update_i.vpn[ $clog2 (CVA6Cfg.SharedTlbDepth) - 1 : 0 ] ][i] = 1'b1 ;
426+ shared_tag_valid_d[vpn_index ][i] = 1'b1 ;
396427 tag_wr_en[i] = 1'b1 ;
397428 pte_wr_en[i] = 1'b1 ;
398429 end
@@ -404,11 +435,11 @@ module cva6_shared_tlb #(
404435 assign shared_tag_wr.vmid = shared_tlb_update_i.vmid;
405436 assign shared_tag_wr.is_page = shared_tlb_update_i.is_page;
406437 assign shared_tag_wr.v_st_enbl = v_st_enbl[i_req_q][HYP_EXT * 2 : 0 ];
407-
408- genvar z ;
438+ 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
439+ genvar z_gen ;
409440 generate
410- for (z = 0 ; z < CVA6Cfg.PtLevels; z ++ ) begin : gen_shared_tag
411- assign shared_tag_wr.vpn[z ] = shared_tlb_update_i.vpn [((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (z + 1 ))- 1 : ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* z )];
441+ for (z_gen = 0 ; z_gen < CVA6Cfg.PtLevels; z_gen ++ ) begin : gen_shared_tag
442+ assign shared_tag_wr.vpn[z_gen ] = vpn_to_store [((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* (z_gen + 1 ))- 1 : ((CVA6Cfg.VpnLen/ CVA6Cfg.PtLevels)* z_gen )];
412443 end
413444 if (CVA6Cfg.RVH ) begin : gen_shared_tag_hyp
414445 // THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4
@@ -420,10 +451,13 @@ module cva6_shared_tlb #(
420451 endgenerate
421452
422453
423- assign tag_wr_addr = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
424454 assign tag_wr_data = shared_tag_wr;
425455
426- assign pte_wr_addr = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
456+ // derive the set index from bits [4 +: clog2] of the (possibly masked) VPN
457+ assign vpn_index = shared_tlb_update_i.vpn[$clog2 (CVA6Cfg.SharedTlbDepth)- 1 : 0 ];
458+ // write the new entry into that set
459+ assign tag_wr_addr = vpn_index;
460+ assign pte_wr_addr = vpn_index;
427461
428462 assign pte_wr_data[0 ] = shared_tlb_update_i.content[CVA6Cfg.XLEN - 1 : 0 ];
429463 assign pte_wr_data[1 ] = shared_tlb_update_i.g_content[CVA6Cfg.XLEN - 1 : 0 ];
0 commit comments