1 // Copyright 2018 ETH Zurich and University of Bologna.
2 // Copyright and related rights are licensed under the Solderpad Hardware
3 // License, Version 0.51 (the "License"); you may not use this file except in
4 // compliance with the License. You may obtain a copy of the License at
5 // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
6 // or agreed to in writing, software, hardware and materials distributed under
7 // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
9 // specific language governing permissions and limitations under the License.
11 // Author: David Schaffenrath, TU Graz
12 // Author: Florian Zaruba, ETH Zurich
14 // Description: Translation Lookaside Buffer, SV39
15 // fully set-associative
17 //import ariane_pkg::*;
20 parameter int TLB_ENTRIES = 4,
21 parameter int ASID_WIDTH = 1
23 input logic clk_i, // Clock
24 input logic rst_ni, // Asynchronous reset active low
25 input logic flush_i, // Flush signal
27 //input tlb_update_t update_i,
29 input logic lu_access_i,
30 input logic [ASID_WIDTH-1:0] lu_asid_i,
31 input logic [63:0] lu_vaddr_i,
32 //output riscv::pte_t lu_content_o,
33 output logic lu_is_2M_o,
34 output logic lu_is_1G_o,
39 // SV39 defines three levels of page tables
41 logic [ASID_WIDTH-1:0] asid;
48 } [TLB_ENTRIES-1:0] tags_q, tags_n;
50 riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
51 logic [8:0] vpn0, vpn1, vpn2;
52 logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
53 logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
57 always_comb begin : translation
58 vpn0 = lu_vaddr_i[20:12];
59 vpn1 = lu_vaddr_i[29:21];
60 vpn2 = lu_vaddr_i[38:30];
63 lu_hit = '{default: 0};
65 lu_content_o = '{default: 0};
69 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
70 // first level match, this may be a giga page, check the ASID flags as well
71 if (tags_q[i].valid && lu_asid_i == tags_q[i].asid && vpn2 == tags_q[i].vpn2) begin
73 if (tags_q[i].is_1G) begin
75 lu_content_o = content_q[i];
78 // not a giga page hit so check further
79 end else if (vpn1 == tags_q[i].vpn1) begin
80 // this could be a 2 mega page hit or a 4 kB hit
82 if (tags_q[i].is_2M || vpn0 == tags_q[i].vpn0) begin
83 lu_is_2M_o = tags_q[i].is_2M;
84 lu_content_o = content_q[i];
96 always_comb begin : update_flush
98 content_n = content_q;
100 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
103 if (lu_asid_i == 1'b0) // flush everything if ASID is 0
104 tags_n[i].valid = 1'b0;
105 else if (lu_asid_i == tags_q[i].asid) // just flush entries from this ASID
106 tags_n[i].valid = 1'b0;
108 // normal replacement
109 end else if (update_i.valid & replace_en[i]) begin
113 vpn2: update_i.vpn [26:18],
114 vpn1: update_i.vpn [17:9],
115 vpn0: update_i.vpn [8:0],
116 is_1G: update_i.is_1G,
117 is_2M: update_i.is_2M,
120 // and content as well
121 content_n[i] = update_i.content;
126 // -----------------------------------------------
127 // PLRU - Pseudo Least Recently Used Replacement
128 // -----------------------------------------------
129 logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
130 always_comb begin : plru_replacement
131 plru_tree_n = plru_tree_q;
132 // The PLRU-tree indexing:
141 // Just predefine which nodes will be set/cleared
142 // E.g. for a TLB with 8 entries, the for-loop is semantically
143 // equivalent to the following pseudo-code:
144 // unique case (1'b1)
145 // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
146 // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
147 // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
148 // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
149 // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
150 // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
151 // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
152 // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
153 // default: begin /* No hit */ end
155 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
156 automatic int unsigned idx_base, shift, new_index;
157 // we got a hit so update the pointer as it was least recently used
158 if (lu_hit[i] & lu_access_i) begin
159 // Set the nodes to the values we would expect
160 for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
161 idx_base = $unsigned((2**lvl)-1);
162 // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
163 shift = $clog2(TLB_ENTRIES) - lvl;
164 // to circumvent the 32 bit integer arithmetic assignment
165 new_index = ~((i >> (shift-1)) & 32'b1);
166 plru_tree_n[idx_base + (i >> shift)] = new_index[0];
170 // Decode tree to write enable signals
171 // Next for-loop basically creates the following logic for e.g. an 8 entry
172 // TLB (note: pseudo-code obviously):
173 // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
174 // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
175 // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
176 // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
177 // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
178 // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
179 // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
180 // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
181 // For each entry traverse the tree. If every tree-node matches,
182 // the corresponding bit of the entry's index, this is
183 // the next entry to replace.
184 for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
186 automatic int unsigned idx_base, shift, new_index;
188 for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
189 idx_base = $unsigned((2**lvl)-1);
190 // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
191 shift = $clog2(TLB_ENTRIES) - lvl;
193 // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
194 new_index = (i >> (shift-1)) & 32'b1;
195 if (new_index[0]) begin
196 en &= plru_tree_q[idx_base + (i>>shift)];
198 en &= ~plru_tree_q[idx_base + (i>>shift)];
205 // sequential process
206 always_ff @(posedge clk_i or negedge rst_ni) begin
208 tags_q <= '{default: 0};
209 content_q <= '{default: 0};
210 plru_tree_q <= '{default: 0};
213 content_q <= content_n;
214 plru_tree_q <= plru_tree_n;
221 //pragma translate_off
224 initial begin : p_assertions
225 assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
226 else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
227 assert (ASID_WIDTH >= 1)
228 else begin $error("ASID width must be at least 1"); $stop(); end
232 function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
233 automatic int count = 0;
234 foreach (vector[idx]) begin
235 count += vector[idx];
240 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
241 else begin $error("More then one hit in TLB!"); $stop(); end
242 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
243 else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
246 //pragma translate_on