alway_comb nested if support
[sv2nmigen.git] / examples / tlb.py
1 # this file has been generated by sv2nmigen
2
3 from nmigen import Signal, Module, Const, Cat, Elaboratable
4
5
6
7 class tlb(Elaboratable):
8
9 def __init__(self):
10 self.clk_i = Signal() # input
11 self.rst_ni = Signal() # input
12 self.flush_i = Signal() # input
13 self.lu_access_i = Signal() # input
14 self.lu_asid_i = Signal(ASID_WIDTH) # input
15 self.lu_vaddr_i = Signal(64) # input
16 self.lu_is_2M_o = Signal() # output
17 self.lu_is_1G_o = Signal() # output
18 self.lu_hit_o = Signal() # output
19 def elaborate(self, platform=None):
20 m = Module()
21 return m
22
23 #// Copyright 2018 ETH Zurich and University of Bologna.
24 #// Copyright and related rights are licensed under the Solderpad Hardware
25 #// License, Version 0.51 (the "License"); you may not use this file except in
26 #// compliance with the License. You may obtain a copy of the License at
27 #// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
28 #// or agreed to in writing, software, hardware and materials distributed under
29 #// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
30 #// CONDITIONS OF ANY KIND, either express or implied. See the License for the
31 #// specific language governing permissions and limitations under the License.
32 #//
33 #// Author: David Schaffenrath, TU Graz
34 #// Author: Florian Zaruba, ETH Zurich
35 #// Date: 21.4.2017
36 #// Description: Translation Lookaside Buffer, SV39
37 #// fully set-associative
38 #
39 #//import ariane_pkg::*;
40 #
41 #module tlb #(
42 # parameter int TLB_ENTRIES = 4,
43 # parameter int ASID_WIDTH = 1
44 # )(
45 # input logic clk_i, // Clock
46 # input logic rst_ni, // Asynchronous reset active low
47 # input logic flush_i, // Flush signal
48 # // Update TLB
49 # //input tlb_update_t update_i,
50 # // Lookup signals
51 # input logic lu_access_i,
52 # input logic [ASID_WIDTH-1:0] lu_asid_i,
53 # input logic [63:0] lu_vaddr_i,
54 # //output riscv::pte_t lu_content_o,
55 # output logic lu_is_2M_o,
56 # output logic lu_is_1G_o,
57 # output logic lu_hit_o
58 #);
59 #
60 """ #docstring_begin
61 // SV39 defines three levels of page tables
62 struct packed {
63 logic [ASID_WIDTH-1:0] asid;
64 logic [8:0] vpn2;
65 logic [8:0] vpn1;
66 logic [8:0] vpn0;
67 logic is_2M;
68 logic is_1G;
69 logic valid;
70 } [TLB_ENTRIES-1:0] tags_q, tags_n;
71
72 riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
73 logic [8:0] vpn0, vpn1, vpn2;
74 logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
75 logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
76 //-------------
77 // Translation
78 //-------------
79 always_comb begin : translation
80 vpn0 = lu_vaddr_i[20:12];
81 vpn1 = lu_vaddr_i[29:21];
82 vpn2 = lu_vaddr_i[38:30];
83
84 // default assignment
85 lu_hit = '{default: 0};
86 lu_hit_o = 1'b0;
87 lu_content_o = '{default: 0};
88 lu_is_1G_o = 1'b0;
89 lu_is_2M_o = 1'b0;
90
91 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
92 // first level match, this may be a giga page, check the ASID flags as well
93 if (tags_q[i].valid && lu_asid_i == tags_q[i].asid && vpn2 == tags_q[i].vpn2) begin
94 // second level
95 if (tags_q[i].is_1G) begin
96 lu_is_1G_o = 1'b1;
97 lu_content_o = content_q[i];
98 lu_hit_o = 1'b1;
99 lu_hit[i] = 1'b1;
100 // not a giga page hit so check further
101 end else if (vpn1 == tags_q[i].vpn1) begin
102 // this could be a 2 mega page hit or a 4 kB hit
103 // output accordingly
104 if (tags_q[i].is_2M || vpn0 == tags_q[i].vpn0) begin
105 lu_is_2M_o = tags_q[i].is_2M;
106 lu_content_o = content_q[i];
107 lu_hit_o = 1'b1;
108 lu_hit[i] = 1'b1;
109 end
110 end
111 end
112 end
113 end
114
115 // ------------------
116 // Update and Flush
117 // ------------------
118 always_comb begin : update_flush
119 tags_n = tags_q;
120 content_n = content_q;
121
122 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
123 if (flush_i) begin
124 // invalidate logic
125 if (lu_asid_i == 1'b0) // flush everything if ASID is 0
126 tags_n[i].valid = 1'b0;
127 else if (lu_asid_i == tags_q[i].asid) // just flush entries from this ASID
128 tags_n[i].valid = 1'b0;
129
130 // normal replacement
131 end else if (update_i.valid & replace_en[i]) begin
132 // update tag array
133 tags_n[i] = '{
134 asid: update_i.asid,
135 vpn2: update_i.vpn [26:18],
136 vpn1: update_i.vpn [17:9],
137 vpn0: update_i.vpn [8:0],
138 is_1G: update_i.is_1G,
139 is_2M: update_i.is_2M,
140 valid: 1'b1
141 };
142 // and content as well
143 content_n[i] = update_i.content;
144 end
145 end
146 end
147
148 // -----------------------------------------------
149 // PLRU - Pseudo Least Recently Used Replacement
150 // -----------------------------------------------
151 logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
152 always_comb begin : plru_replacement
153 plru_tree_n = plru_tree_q;
154 // The PLRU-tree indexing:
155 // lvl0 0
156 // / \
157 // / \
158 // lvl1 1 2
159 // / \ / \
160 // lvl2 3 4 5 6
161 // / \ /\/\ /\
162 // ... ... ... ...
163 // Just predefine which nodes will be set/cleared
164 // E.g. for a TLB with 8 entries, the for-loop is semantically
165 // equivalent to the following pseudo-code:
166 // unique case (1'b1)
167 // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
168 // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
169 // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
170 // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
171 // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
172 // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
173 // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
174 // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
175 // default: begin /* No hit */ end
176 // endcase
177 for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
178 automatic int unsigned idx_base, shift, new_index;
179 // we got a hit so update the pointer as it was least recently used
180 if (lu_hit[i] & lu_access_i) begin
181 // Set the nodes to the values we would expect
182 for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
183 idx_base = $unsigned((2**lvl)-1);
184 // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
185 shift = $clog2(TLB_ENTRIES) - lvl;
186 // to circumvent the 32 bit integer arithmetic assignment
187 new_index = ~((i >> (shift-1)) & 32'b1);
188 plru_tree_n[idx_base + (i >> shift)] = new_index[0];
189 end
190 end
191 end
192 // Decode tree to write enable signals
193 // Next for-loop basically creates the following logic for e.g. an 8 entry
194 // TLB (note: pseudo-code obviously):
195 // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
196 // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
197 // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
198 // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
199 // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
200 // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
201 // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
202 // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
203 // For each entry traverse the tree. If every tree-node matches,
204 // the corresponding bit of the entry's index, this is
205 // the next entry to replace.
206 for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
207 automatic logic en;
208 automatic int unsigned idx_base, shift, new_index;
209 en = 1'b1;
210 for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
211 idx_base = $unsigned((2**lvl)-1);
212 // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
213 shift = $clog2(TLB_ENTRIES) - lvl;
214
215 // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
216 new_index = (i >> (shift-1)) & 32'b1;
217 if (new_index[0]) begin
218 en &= plru_tree_q[idx_base + (i>>shift)];
219 end else begin
220 en &= ~plru_tree_q[idx_base + (i>>shift)];
221 end
222 end
223 replace_en[i] = en;
224 end
225 end
226
227 // sequential process
228 always_ff @(posedge clk_i or negedge rst_ni) begin
229 if(~rst_ni) begin
230 tags_q <= '{default: 0};
231 content_q <= '{default: 0};
232 plru_tree_q <= '{default: 0};
233 end else begin
234 tags_q <= tags_n;
235 content_q <= content_n;
236 plru_tree_q <= plru_tree_n;
237 end
238 end
239 //--------------
240 // Sanity checks
241 //--------------
242
243 //pragma translate_off
244 `ifndef VERILATOR
245
246 initial begin : p_assertions
247 assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
248 else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
249 assert (ASID_WIDTH >= 1)
250 else begin $error("ASID width must be at least 1"); $stop(); end
251 end
252
253 // Just for checking
254 function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
255 automatic int count = 0;
256 foreach (vector[idx]) begin
257 count += vector[idx];
258 end
259 return count;
260 endfunction
261
262 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
263 else begin $error("More then one hit in TLB!"); $stop(); end
264 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
265 else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
266
267 `endif
268 //pragma translate_on
269 """
270 #endmodule
271 #
272 #