convert numbers to python format
[sv2nmigen.git] / examples / tlb.sv
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.
10 //
11 // Author: David Schaffenrath, TU Graz
12 // Author: Florian Zaruba, ETH Zurich
13 // Date: 21.4.2017
14 // Description: Translation Lookaside Buffer, SV39
15 // fully set-associative
16
17 //import ariane_pkg::*;
18
19 module tlb #(
20 parameter int TLB_ENTRIES = 4,
21 parameter int ASID_WIDTH = 1
22 )(
23 input logic clk_i, // Clock
24 input logic rst_ni, // Asynchronous reset active low
25 input logic flush_i, // Flush signal
26 // Update TLB
27 //input tlb_update_t update_i,
28 // Lookup signals
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,
35 output logic lu_hit_o
36 );
37
38 #docstring_begin
39 // SV39 defines three levels of page tables
40 struct packed {
41 logic [ASID_WIDTH-1:0] asid;
42 logic [8:0] vpn2;
43 logic [8:0] vpn1;
44 logic [8:0] vpn0;
45 logic is_2M;
46 logic is_1G;
47 logic valid;
48 } [TLB_ENTRIES-1:0] tags_q, tags_n;
49
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
54 //-------------
55 // Translation
56 //-------------
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];
61
62 // default assignment
63 lu_hit = '{default: 0};
64 lu_hit_o = 1'b0;
65 lu_content_o = '{default: 0};
66 lu_is_1G_o = 1'b0;
67 lu_is_2M_o = 1'b0;
68
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
72 // second level
73 if (tags_q[i].is_1G) begin
74 lu_is_1G_o = 1'b1;
75 lu_content_o = content_q[i];
76 lu_hit_o = 1'b1;
77 lu_hit[i] = 1'b1;
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
81 // output accordingly
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];
85 lu_hit_o = 1'b1;
86 lu_hit[i] = 1'b1;
87 end
88 end
89 end
90 end
91 end
92
93 // ------------------
94 // Update and Flush
95 // ------------------
96 always_comb begin : update_flush
97 tags_n = tags_q;
98 content_n = content_q;
99
100 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
101 if (flush_i) begin
102 // invalidate logic
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;
107
108 // normal replacement
109 end else if (update_i.valid & replace_en[i]) begin
110 // update tag array
111 tags_n[i] = '{
112 asid: update_i.asid,
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,
118 valid: 1'b1
119 };
120 // and content as well
121 content_n[i] = update_i.content;
122 end
123 end
124 end
125
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:
133 // lvl0 0
134 // / \
135 // / \
136 // lvl1 1 2
137 // / \ / \
138 // lvl2 3 4 5 6
139 // / \ /\/\ /\
140 // ... ... ... ...
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
154 // endcase
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];
167 end
168 end
169 end
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
185 automatic logic en;
186 automatic int unsigned idx_base, shift, new_index;
187 en = 1'b1;
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;
192
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)];
197 end else begin
198 en &= ~plru_tree_q[idx_base + (i>>shift)];
199 end
200 end
201 replace_en[i] = en;
202 end
203 end
204
205 // sequential process
206 always_ff @(posedge clk_i or negedge rst_ni) begin
207 if(~rst_ni) begin
208 tags_q <= '{default: 0};
209 content_q <= '{default: 0};
210 plru_tree_q <= '{default: 0};
211 end else begin
212 tags_q <= tags_n;
213 content_q <= content_n;
214 plru_tree_q <= plru_tree_n;
215 end
216 end
217 //--------------
218 // Sanity checks
219 //--------------
220
221 //pragma translate_off
222 `ifndef VERILATOR
223
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
229 end
230
231 // Just for checking
232 function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
233 automatic int count = 0;
234 foreach (vector[idx]) begin
235 count += vector[idx];
236 end
237 return count;
238 endfunction
239
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
244
245 `endif
246 //pragma translate_on
247 #docstring_end
248 endmodule