1 |
201 |
dgisselq |
////////////////////////////////////////////////////////////////////////////////
|
2 |
|
|
//
|
3 |
|
|
// Filename: zipmmu.v
|
4 |
|
|
//
|
5 |
|
|
// Project: Zip CPU backend for the GNU Compiler Collection
|
6 |
|
|
//
|
7 |
|
|
// Purpose: To provide a "bump-in-the-line" wishbone memory management
|
8 |
|
|
// unit, that is configured from one wishbone bus and modifies a
|
9 |
|
|
// separate wishbone bus. Both busses will not be active at the same time.
|
10 |
|
|
//
|
11 |
209 |
dgisselq |
// The idea is that the CPU can use one portion of its peripheral
|
12 |
201 |
dgisselq |
// system memory space to configure the MMU, and another portion of its
|
13 |
|
|
// memory space to access the MMU. Even more, configuring the MMU is to
|
14 |
|
|
// be done when the CPU is in supervisor mode. This means that all
|
15 |
|
|
// high-memory, system-peripheral accesses will be enabled *only* when
|
16 |
|
|
// the CPU is in supervisor mode.
|
17 |
|
|
//
|
18 |
|
|
// There is a very specific reason for this design choice: by designing
|
19 |
|
|
// the MMU in this fashion, the MMU may then be inluded (or not) at the
|
20 |
|
|
// discretion of the individual assembling the ZipSystem (or equivalent)
|
21 |
|
|
// module.
|
22 |
|
|
//
|
23 |
|
|
// Design Goals:
|
24 |
|
|
//
|
25 |
|
|
// Since we're trying to design this for disadvantaged, limited CPUs,
|
26 |
|
|
// we should be able to offer such CPUs only as much MMU as they want.
|
27 |
|
|
// Therefore, it should be possible to scale the MMU up and/or down in
|
28 |
|
|
// LUT space.
|
29 |
|
|
//
|
30 |
|
|
// Memory space:
|
31 |
|
|
// 1. On access via the memory bus, the MMU should provide for a speed
|
32 |
|
|
// going through it such that any access is delayed by only one
|
33 |
|
|
// clock cycle. Further, multiple accesses to the same page
|
34 |
|
|
// should not take any longer than the one cycle delay. Accesses
|
35 |
|
|
// to other pages should take a minimum number of clocks.
|
36 |
209 |
dgisselq |
// Accesses from one page to the next, such as from one page to
|
37 |
201 |
dgisselq |
// the next subsequent one, should cost no delays.
|
38 |
|
|
//
|
39 |
|
|
// 2. One independent control word to set the current context
|
40 |
|
|
//
|
41 |
|
|
// - When context = 0, virtual page = physical page, page table is an
|
42 |
|
|
// unused pass through.
|
43 |
|
|
// - When context != 0, MMU translation is active anytime the GIE is
|
44 |
|
|
// set. Pages must match context, as well as virtual address.
|
45 |
|
|
//
|
46 |
|
|
// - Contains 4 RdOnly bits indicating the log address size for the
|
47 |
|
|
// machine, offset by 17. Thus, the build will have an address
|
48 |
|
|
// bus of width (lgpage+17), or a memory space of (2^(lgpage+17)).
|
49 |
|
|
// Under this formula, the number of valid address bits can range
|
50 |
|
|
// from 17 to 32.
|
51 |
209 |
dgisselq |
// - Contains 4 RdOnly bits indicating log_2 TLB table size.
|
52 |
201 |
dgisselq |
// Size is given by (2^(lgsize)). I'm considering sizes of 6,7&8
|
53 |
|
|
// - Contains 4 RdOnly bits indicating the log page size, offset by
|
54 |
|
|
// eight. Page sizes are therefore given by (2^(lgpage+8)), and
|
55 |
|
|
// the smallest page size is 256 words.
|
56 |
|
|
// - Contains 4 RdOnly bits indicating the log context size, offset by 1.
|
57 |
|
|
// The number of bits in the context word therefore run from 1 to
|
58 |
|
|
// (lgcontext+1)-1, supporting between (2^1)-1=3 and
|
59 |
|
|
// (2^16)-1 = 65535 contexts. (The zero context is not being
|
60 |
|
|
// counted here, as it is special.)
|
61 |
|
|
//
|
62 |
|
|
// +------+------+------+------+--------------------+
|
63 |
|
|
// | | | | | |
|
64 |
|
|
// | 4b | 4b | 4b | 4b | 16-bit |
|
65 |
|
|
// | LGADR| LGTBL|LGPGSZ|LGCTXT| Context word |
|
66 |
|
|
// | | | | | |
|
67 |
|
|
// +------+------+------+------+--------------------+
|
68 |
|
|
//
|
69 |
|
|
// Supervisor *cannot* have page table entries, since there are no
|
70 |
|
|
// interrupts (page faults) allowed in supervisor context.
|
71 |
|
|
//
|
72 |
209 |
dgisselq |
// To be valid,
|
73 |
201 |
dgisselq |
// Context Size (1..16), NFlags ( 4) < Page Size (8-23 bits)
|
74 |
|
|
// Page size (8-23 bits) > NFlags bits (4)
|
75 |
|
|
//
|
76 |
|
|
// Small page sizes, then, mean fewer contexts are possible
|
77 |
|
|
//
|
78 |
|
|
// 3. One status word, which contains the address that failed and some
|
79 |
|
|
// flags:
|
80 |
|
|
//
|
81 |
209 |
dgisselq |
// Top Virtual address bits indicate which page ... caused a problem.
|
82 |
201 |
dgisselq |
// These will be the top N bits of the word, where N is the size
|
83 |
|
|
// of the virtual address bits. (Bits are cleared upon any write.)
|
84 |
|
|
//
|
85 |
|
|
// Flags: (Up to 12 bits, all zeros means no fault. Bits are cleared upon
|
86 |
|
|
// write)
|
87 |
|
|
// - 4: Multiple page table matches
|
88 |
|
|
// - 2: Attempt to write a read-only page
|
89 |
|
|
// - 1: Page not found
|
90 |
209 |
dgisselq |
//
|
91 |
201 |
dgisselq |
// 3. Two words per active page table entry, accessed through two bus
|
92 |
|
|
// addresses. This word contains:
|
93 |
|
|
//
|
94 |
|
|
// 16-bits Page context
|
95 |
|
|
// 20-bits Virtual address
|
96 |
|
|
// 20-bits Physical address
|
97 |
|
|
// A physical address of all ones means that the
|
98 |
|
|
// page does not exist, and any attempt to access
|
99 |
|
|
// the virtual address associated with this page
|
100 |
|
|
// should fault.
|
101 |
|
|
//
|
102 |
|
|
// Flags:
|
103 |
|
|
// 1-bit Read-only / ~written (user set/read/written)
|
104 |
|
|
// If set, this page will cause a fault on any
|
105 |
|
|
// attempt to write this memory.
|
106 |
209 |
dgisselq |
// 1-bit This page may be executed
|
107 |
|
|
// 1-bit Cacheable
|
108 |
|
|
// This is not a hardware page, but a memory page.
|
109 |
|
|
// Therefore, the values within this page may be
|
110 |
|
|
// cached.
|
111 |
201 |
dgisselq |
// 1-bit Accessed
|
112 |
|
|
// This an be used to implement a least-recently
|
113 |
|
|
// used measure. The hardware will set this value
|
114 |
|
|
// when the page is accessed. The user can also
|
115 |
|
|
// set or clear this at will.
|
116 |
|
|
//
|
117 |
|
|
// (Loaded flag Not necessary, just map the physical page to 0)
|
118 |
|
|
//
|
119 |
|
|
// We could equivalently do a 16-bit V&P addresses, for a 28-bit total
|
120 |
|
|
// address space, if we didn't want to support the entire 32-bit space.
|
121 |
|
|
//
|
122 |
|
|
//
|
123 |
|
|
// 4. Can read/write this word in two parts:
|
124 |
|
|
//
|
125 |
209 |
dgisselq |
// (20-bit Virtual )(8-bits lower context)(4-bit flags), and
|
126 |
201 |
dgisselq |
// (20-bit Physical)(8-bits upper context)(4-bit flags)
|
127 |
|
|
//
|
128 |
|
|
// Actual bit lengths will vary as the MMU configuration changes,
|
129 |
|
|
// however the flags will always be the low order four bits,
|
130 |
|
|
// and the virtual/physical address flags will always consume
|
131 |
|
|
// 32 bits minus the page table size. The context bits will
|
132 |
|
|
// always be split into upper and lower context bits. If there
|
133 |
|
|
// are more context bits than can fit in the space, then the
|
134 |
|
|
// upper bits of the context field will be filled with zeros.
|
135 |
|
|
//
|
136 |
|
|
// On any write, the context bits will be set from the context
|
137 |
|
|
// bits in the control register.
|
138 |
|
|
//
|
139 |
|
|
// +----+----+-----+----+----+----+----+--+--+--+--+
|
140 |
209 |
dgisselq |
// | | Lower 8b| R| E| C| A|
|
141 |
|
|
// | 20-bit Virtual page ID | Context | O| X| C| C|
|
142 |
|
|
// |(top 20 bits of the addr)| ID | n| E| H| C|
|
143 |
|
|
// | | | W| F| E| S|
|
144 |
201 |
dgisselq |
// +----+----+-----+----+----+----+----+--+--+--+--+
|
145 |
|
|
//
|
146 |
|
|
// +----+----+-----+----+----+----+----+--+--+--+--+
|
147 |
|
|
// | | Upper 8b| R| A| C| T|
|
148 |
|
|
// | 20-bit Physical pg ID | Context | O| C| C| H|
|
149 |
|
|
// |(top 20 bits of the | ID | n| C| H| S|
|
150 |
209 |
dgisselq |
// | physical address) | | W| S| E| P|
|
151 |
201 |
dgisselq |
// +----+----+-----+----+----+----+----+--+--+--+--+
|
152 |
|
|
//
|
153 |
|
|
// 5. PF Cache--handles words in both physical and virtual
|
154 |
|
|
// - On any pf-read, the MMU returns the current pagetable/TBL mapping
|
155 |
|
|
// This consists of [Context,Va,Pa].
|
156 |
|
|
// - The PF cache stores this with the address tag. (If the PF is reading,
|
157 |
|
|
// the VP should match, only the physical page ID needs to be
|
158 |
|
|
// sored ...)
|
159 |
|
|
// - At the end of any cache line read, the page table/TBL mapping address
|
160 |
|
|
// will have long been available, the "Valid" bit will be turned
|
161 |
|
|
// on and associated with the physical mapping.
|
162 |
|
|
// - On any data-write (pf doesn't write), MMU sends [Context,Va,Pa]
|
163 |
209 |
dgisselq |
// TLB mapping to the pf-cache.
|
164 |
201 |
dgisselq |
// - If the write matches any physical PF-cache addresses (???), the
|
165 |
|
|
// pfcache declares that address line invalid, and just plain
|
166 |
|
|
// clears the valid bit for that page.
|
167 |
|
|
//
|
168 |
|
|
// Since the cache lines sizes are smaller than the page table sizes,
|
169 |
|
|
// failure to match the address means ... what?
|
170 |
|
|
//
|
171 |
|
|
//
|
172 |
|
|
// 6. Normal operation and timing:
|
173 |
|
|
// - One clock lost if still on the same page as last time, or in the
|
174 |
|
|
// supervisor (physical pages only) context ...
|
175 |
|
|
// - Two clocks (1-more delay) if opening a new page.
|
176 |
|
|
// (1-clock to look up the entry--comparing against all entries,
|
177 |
|
|
// 1-clock to read it, next clock the access goes forward.)
|
178 |
|
|
// - No more than two stalls for any access, pipelineable. Thus, once
|
179 |
|
|
// you've stalled by both clocks, you'll not stall again during
|
180 |
|
|
// any pipeline operation.
|
181 |
|
|
//
|
182 |
|
|
//
|
183 |
|
|
//
|
184 |
|
|
// Creator: Dan Gisselquist, Ph.D.
|
185 |
|
|
// Gisselquist Technology, LLC
|
186 |
|
|
//
|
187 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
188 |
|
|
//
|
189 |
209 |
dgisselq |
// Copyright (C) 2016-2019, Gisselquist Technology, LLC
|
190 |
201 |
dgisselq |
//
|
191 |
|
|
// This program is free software (firmware): you can redistribute it and/or
|
192 |
|
|
// modify it under the terms of the GNU General Public License as published
|
193 |
|
|
// by the Free Software Foundation, either version 3 of the License, or (at
|
194 |
|
|
// your option) any later version.
|
195 |
|
|
//
|
196 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
197 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
|
198 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
199 |
|
|
// for more details.
|
200 |
|
|
//
|
201 |
|
|
// You should have received a copy of the GNU General Public License along
|
202 |
|
|
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
|
203 |
|
|
// target there if the PDF file isn't present.) If not, see
|
204 |
|
|
// <http://www.gnu.org/licenses/> for a copy.
|
205 |
|
|
//
|
206 |
|
|
// License: GPL, v3, as defined and found on www.gnu.org,
|
207 |
|
|
// http://www.gnu.org/licenses/gpl.html
|
208 |
|
|
//
|
209 |
|
|
//
|
210 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
211 |
209 |
dgisselq |
//
|
212 |
|
|
//
|
213 |
|
|
`default_nettype none
|
214 |
|
|
//
|
215 |
|
|
`define ROFLAG 3 // Read-only flag
|
216 |
|
|
`define EXEFLG 2 // No-execute flag (invalid for I-cache)
|
217 |
|
|
`define CHFLAG 1 // Cachable flag
|
218 |
|
|
`define AXFLAG 0 // Accessed flag
|
219 |
|
|
//
|
220 |
|
|
module zipmmu(i_clk, i_reset, i_wbs_cyc_stb, i_wbs_we, i_wbs_addr,
|
221 |
|
|
i_wbs_data, o_wbs_ack, o_wbs_stall, o_wbs_data,
|
222 |
|
|
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_exe,
|
223 |
|
|
i_wbm_addr, i_wbm_data, i_wbm_sel, i_gie,
|
224 |
|
|
o_cyc, o_stb, o_we, o_addr, o_data, o_sel,
|
225 |
201 |
dgisselq |
i_stall, i_ack, i_err, i_data,
|
226 |
|
|
o_rtn_stall, o_rtn_ack, o_rtn_err,
|
227 |
|
|
o_rtn_miss, o_rtn_data,
|
228 |
|
|
pf_return_stb, pf_return_we,
|
229 |
|
|
pf_return_p, pf_return_v,
|
230 |
|
|
pf_return_cachable);
|
231 |
209 |
dgisselq |
parameter // The size of the address bus. Actual addressable
|
232 |
|
|
// size will likely be 2^(ADDRESS_WIDTH+2) octets
|
233 |
|
|
ADDRESS_WIDTH=28,
|
234 |
|
|
// Number of page table entries
|
235 |
|
|
`ifdef FORMAL
|
236 |
|
|
LGTBL=4'h2,
|
237 |
|
|
`else
|
238 |
|
|
LGTBL=4'h6,
|
239 |
|
|
`endif
|
240 |
|
|
// The requested log page size in 8-bit bytes
|
241 |
|
|
PLGPGSZB=20,
|
242 |
|
|
// Number of bits describing context
|
243 |
|
|
`ifdef FORMAL
|
244 |
|
|
PLGCTXT=2;
|
245 |
|
|
`else
|
246 |
|
|
PLGCTXT=16;
|
247 |
|
|
`endif
|
248 |
|
|
parameter [0:0] OPT_DELAY_RETURN = 1'b0;
|
249 |
201 |
dgisselq |
localparam // And for our derived parameters (don't set these ...)
|
250 |
209 |
dgisselq |
// Width of the data bus is 32-bits. This may be hard
|
251 |
|
|
// to change.
|
252 |
|
|
DW = 32,
|
253 |
201 |
dgisselq |
// AW is just shorthand for the name ADDRESS_WIDTH
|
254 |
|
|
AW = ADDRESS_WIDTH,
|
255 |
|
|
// Page sizes must allow for a minimum of one context
|
256 |
|
|
// bit per page, plus four flag bits, hence the minimum
|
257 |
|
|
// number of bits for an address within a page is 5
|
258 |
209 |
dgisselq |
LGPGSZB=(PLGPGSZB < 5)? 5:PLGPGSZB, // in bytes
|
259 |
|
|
LGPGSZW=LGPGSZB-2, // in words
|
260 |
|
|
// The context value for a given page can be split
|
261 |
|
|
// across both virtual and physical words. It cannot
|
262 |
|
|
// have so many bits to it that it takes more bits
|
263 |
|
|
// then are available.
|
264 |
|
|
LGCTXT=((2*LGPGSZB-4)>PLGCTXT)?
|
265 |
|
|
PLGCTXT:(2*LGPGSZB-4),
|
266 |
201 |
dgisselq |
// LGLCTX is the number of context bits in the low word
|
267 |
209 |
dgisselq |
LGLCTX=(LGCTXT > (LGPGSZB-4))?(LGPGSZB-4):LGCTXT,
|
268 |
201 |
dgisselq |
// LGHCTX is the number of context bits in the high word
|
269 |
209 |
dgisselq |
LGHCTX= (LGCTXT-LGLCTX>0)?(LGCTXT-LGLCTX):0,
|
270 |
|
|
VAW=(DW-LGPGSZB), // Virtual address width, in bytes
|
271 |
|
|
PAW=(AW-LGPGSZW), // Physical address width, in words
|
272 |
201 |
dgisselq |
TBL_BITS = LGTBL, // Bits necessary to addr tbl
|
273 |
|
|
TBL_SIZE=(1<<TBL_BITS);// Number of table entries
|
274 |
209 |
dgisselq |
input wire i_clk, i_reset;
|
275 |
201 |
dgisselq |
//
|
276 |
209 |
dgisselq |
input wire i_wbs_cyc_stb;
|
277 |
|
|
input wire i_wbs_we;
|
278 |
|
|
input wire [(LGTBL+1):0] i_wbs_addr;
|
279 |
|
|
input wire [(DW-1):0] i_wbs_data;
|
280 |
|
|
output reg o_wbs_ack;
|
281 |
|
|
output wire o_wbs_stall;
|
282 |
|
|
output reg [(DW-1):0] o_wbs_data;
|
283 |
201 |
dgisselq |
//
|
284 |
209 |
dgisselq |
input wire i_wbm_cyc, i_wbm_stb;
|
285 |
201 |
dgisselq |
//
|
286 |
209 |
dgisselq |
input wire i_wbm_we, i_wbm_exe;
|
287 |
|
|
input wire [(DW-2-1):0] i_wbm_addr;
|
288 |
|
|
input wire [(DW-1):0] i_wbm_data;
|
289 |
|
|
input wire [(DW/8-1):0] i_wbm_sel;
|
290 |
|
|
input wire i_gie;
|
291 |
201 |
dgisselq |
//
|
292 |
|
|
// Here's where we drive the slave side of the bus
|
293 |
|
|
output reg o_cyc;
|
294 |
|
|
output wire o_stb, o_we;
|
295 |
|
|
output reg [(AW-1):0] o_addr;
|
296 |
|
|
output reg [(DW-1):0] o_data;
|
297 |
209 |
dgisselq |
output reg [(DW/8-1):0] o_sel;
|
298 |
201 |
dgisselq |
// and get our return information from driving the slave ...
|
299 |
209 |
dgisselq |
input wire i_stall, i_ack, i_err;
|
300 |
|
|
input wire [(DW-1):0] i_data;
|
301 |
201 |
dgisselq |
//
|
302 |
|
|
// Here's where we return information on either our slave/control bus
|
303 |
|
|
// or the memory bus we are controlled from. Note that we share these
|
304 |
|
|
// wires ...
|
305 |
|
|
output wire o_rtn_stall;
|
306 |
209 |
dgisselq |
output wire o_rtn_ack;
|
307 |
201 |
dgisselq |
output wire o_rtn_err, o_rtn_miss;
|
308 |
209 |
dgisselq |
output wire [(DW-1):0] o_rtn_data;
|
309 |
201 |
dgisselq |
// Finally, to allow the prefetch to snoop on the MMU conversion ...
|
310 |
|
|
output wire pf_return_stb, // snoop data is valid
|
311 |
|
|
pf_return_we; // snoop data is chnging
|
312 |
|
|
output wire [(PAW-1):0] pf_return_p;
|
313 |
|
|
output wire [(VAW-1):0] pf_return_v;
|
314 |
|
|
output wire pf_return_cachable;
|
315 |
|
|
//
|
316 |
|
|
//
|
317 |
|
|
|
318 |
|
|
//
|
319 |
|
|
//
|
320 |
|
|
//
|
321 |
209 |
dgisselq |
reg [3:0] tlb_flags [0:(TBL_SIZE-1)];
|
322 |
|
|
wire [3:0] s_tlb_flags;
|
323 |
201 |
dgisselq |
reg [(LGCTXT-1):0] tlb_cdata [0:(TBL_SIZE-1)];
|
324 |
209 |
dgisselq |
reg [(VAW-1):0] tlb_vdata [0:(TBL_SIZE-1)];
|
325 |
|
|
reg [(PAW-1):0] tlb_pdata [0:(TBL_SIZE-1)];
|
326 |
|
|
reg [(TBL_SIZE-1):0] tlb_valid, tlb_accessed;
|
327 |
201 |
dgisselq |
|
328 |
209 |
dgisselq |
wire adr_control, adr_vtable, adr_ptable;
|
329 |
|
|
wire wr_control, wr_vtable, wr_ptable;
|
330 |
201 |
dgisselq |
wire [(LGTBL-1):0] wr_tlb_addr;
|
331 |
209 |
dgisselq |
assign wr_tlb_addr= i_wbs_addr[(LGTBL):1]; // Leave bottom for V/P
|
332 |
|
|
assign adr_control= (i_wbs_cyc_stb)&&(~i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
|
333 |
|
|
assign adr_vtable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
|
334 |
|
|
assign adr_ptable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&( i_wbs_addr[0]);
|
335 |
|
|
assign wr_control = (adr_control)&&(i_wbs_we);
|
336 |
|
|
assign wr_vtable = (adr_vtable )&&(i_wbs_we);
|
337 |
|
|
assign wr_ptable = (adr_ptable )&&(i_wbs_we);
|
338 |
201 |
dgisselq |
|
339 |
209 |
dgisselq |
reg z_context;
|
340 |
|
|
wire kernel_context;
|
341 |
|
|
reg [(LGCTXT-1):0] r_context_word;
|
342 |
201 |
dgisselq |
//
|
343 |
209 |
dgisselq |
wire [31:0] w_control_data, w_ptable_reg;
|
344 |
|
|
reg [31:0] w_vtable_reg;
|
345 |
201 |
dgisselq |
reg [31:0] status_word;
|
346 |
|
|
//
|
347 |
|
|
//
|
348 |
209 |
dgisselq |
reg r_pending, r_we, r_exe, r_valid,
|
349 |
|
|
last_page_valid, last_ro, last_exe;
|
350 |
|
|
reg [(DW-3):0] r_addr;
|
351 |
201 |
dgisselq |
reg [(DW-1):0] r_data;
|
352 |
209 |
dgisselq |
wire [(VAW-1):0] vpage;
|
353 |
|
|
wire [AW-LGPGSZW-1:0] ppage;
|
354 |
|
|
reg [(DW/8-1):0] r_sel;
|
355 |
201 |
dgisselq |
reg [(PAW-1):0] last_ppage;
|
356 |
|
|
reg [(VAW-1):0] last_vpage;
|
357 |
|
|
//
|
358 |
|
|
wire [(TBL_SIZE-1):0] r_tlb_match;
|
359 |
209 |
dgisselq |
reg [(LGTBL-1):0] s_tlb_addr, last_tlb;
|
360 |
201 |
dgisselq |
reg s_tlb_miss, s_tlb_hit, s_pending;
|
361 |
|
|
//
|
362 |
209 |
dgisselq |
wire ro_flag, exe_flag, simple_miss, ro_miss, exe_miss, table_err, cachable;
|
363 |
|
|
reg pf_stb, pf_cachable;
|
364 |
|
|
reg miss_pending;
|
365 |
201 |
dgisselq |
//
|
366 |
|
|
reg rtn_err;
|
367 |
|
|
|
368 |
|
|
|
369 |
209 |
dgisselq |
wire this_page_valid, pending_page_valid;
|
370 |
|
|
assign this_page_valid = ((last_page_valid)
|
371 |
|
|
&&(i_wbm_addr[(DW-3):(DW-2-VAW)]==last_vpage)
|
372 |
|
|
&&((!last_ro)||(!i_wbm_we))
|
373 |
|
|
&&((!last_exe)||(!i_wbm_exe)));
|
374 |
|
|
assign pending_page_valid = ((s_pending)&&(s_tlb_hit)
|
375 |
|
|
&&((!r_we)||(!ro_flag))
|
376 |
|
|
&&((!r_exe)||(exe_flag)));
|
377 |
|
|
|
378 |
201 |
dgisselq |
//////////////////////////////////////////
|
379 |
|
|
//
|
380 |
|
|
//
|
381 |
209 |
dgisselq |
// Step one -- handle the control bus--i_wbs_cyc_stb
|
382 |
201 |
dgisselq |
//
|
383 |
|
|
//
|
384 |
|
|
//////////////////////////////////////////
|
385 |
|
|
always @(posedge i_clk)
|
386 |
|
|
begin
|
387 |
|
|
// Write to the Translation lookaside buffer
|
388 |
|
|
if (wr_vtable)
|
389 |
209 |
dgisselq |
tlb_vdata[wr_tlb_addr]<=i_wbs_data[(DW-1):LGPGSZB];
|
390 |
201 |
dgisselq |
if (wr_ptable)
|
391 |
209 |
dgisselq |
tlb_pdata[wr_tlb_addr]<=i_wbs_data[(AW+1):LGPGSZB];
|
392 |
201 |
dgisselq |
// Set the context register for the page
|
393 |
|
|
if (wr_vtable)
|
394 |
209 |
dgisselq |
tlb_flags[wr_tlb_addr] <= { i_wbs_data[3:1], 1'b0 };
|
395 |
|
|
if (wr_vtable)
|
396 |
|
|
tlb_cdata[wr_tlb_addr][(LGLCTX-1):0]
|
397 |
|
|
<= i_wbs_data[(LGLCTX+4-1):4];
|
398 |
201 |
dgisselq |
end
|
399 |
209 |
dgisselq |
|
400 |
|
|
initial tlb_accessed = 0;
|
401 |
|
|
always @(posedge i_clk)
|
402 |
|
|
if (i_reset)
|
403 |
|
|
tlb_accessed <= 0;
|
404 |
|
|
else begin
|
405 |
|
|
if (wr_vtable)
|
406 |
|
|
tlb_accessed[wr_tlb_addr] <= 1'b0;
|
407 |
|
|
// Otherwise, keep track of the accessed bit if we
|
408 |
|
|
// ever access this page
|
409 |
|
|
else if ((!kernel_context)&&(pending_page_valid))
|
410 |
|
|
tlb_accessed[s_tlb_addr] <= 1'b1;
|
411 |
|
|
else if ((!kernel_context)&&(this_page_valid))
|
412 |
|
|
tlb_accessed[last_tlb] <= 1'b1;
|
413 |
|
|
end
|
414 |
|
|
|
415 |
|
|
generate if (LGHCTX > 0)
|
416 |
|
|
begin : HCTX
|
417 |
|
|
always @(posedge i_clk)
|
418 |
|
|
if (wr_ptable)
|
419 |
|
|
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX]
|
420 |
|
|
<= i_wbs_data[(LGHCTX+4-1):4];
|
421 |
|
|
end endgenerate
|
422 |
|
|
|
423 |
201 |
dgisselq |
// Writing to the control word
|
424 |
|
|
initial z_context = 1'b1;
|
425 |
|
|
initial r_context_word = 0;
|
426 |
|
|
always @(posedge i_clk)
|
427 |
|
|
if (wr_control)
|
428 |
|
|
begin
|
429 |
209 |
dgisselq |
r_context_word <= i_wbs_data[(LGCTXT-1):0];
|
430 |
|
|
z_context <= (i_wbs_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
|
431 |
201 |
dgisselq |
end
|
432 |
209 |
dgisselq |
assign kernel_context = (z_context)||(!i_gie);
|
433 |
201 |
dgisselq |
// Status words cannot be written to
|
434 |
|
|
|
435 |
209 |
dgisselq |
always @(posedge i_clk)
|
436 |
|
|
if (i_reset)
|
437 |
|
|
tlb_valid <= 0;
|
438 |
|
|
else if (wr_ptable)
|
439 |
|
|
tlb_valid[wr_tlb_addr]<=1'b1; //(i_wbs_data[(AW+1):LGPGSZB]!=0);
|
440 |
|
|
|
441 |
|
|
/* v*rilator lint_off WIDTH */
|
442 |
|
|
assign w_control_data[31:28] = AW[3:0]-4'd1;
|
443 |
|
|
assign w_control_data[27:24] = LGTBL[3:0];
|
444 |
|
|
assign w_control_data[23:20] = LGPGSZB[3:0]-4'd10;
|
445 |
|
|
assign w_control_data[19:16] = LGCTXT[3:0]-1'b1;
|
446 |
|
|
/* v*rilator lint_on WIDTH */
|
447 |
201 |
dgisselq |
assign w_control_data[15: 0] = {{(16-LGCTXT){1'b0}}, r_context_word};
|
448 |
|
|
//
|
449 |
209 |
dgisselq |
always @(*)
|
450 |
|
|
begin
|
451 |
|
|
w_vtable_reg = 0;
|
452 |
|
|
w_vtable_reg[(DW-1):LGPGSZB] = tlb_vdata[wr_tlb_addr];
|
453 |
|
|
w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] };
|
454 |
|
|
w_vtable_reg[ 3:0] = { tlb_flags[wr_tlb_addr][3:1],
|
455 |
|
|
tlb_accessed[wr_tlb_addr] };
|
456 |
|
|
end
|
457 |
201 |
dgisselq |
//
|
458 |
209 |
dgisselq |
assign w_ptable_reg[(DW-1):LGPGSZB] = { {(DW-PAW-LGPGSZB){1'b0}},
|
459 |
201 |
dgisselq |
tlb_pdata[wr_tlb_addr] };
|
460 |
209 |
dgisselq |
assign w_ptable_reg[ 3:0] = 4'h0;
|
461 |
201 |
dgisselq |
//
|
462 |
|
|
generate
|
463 |
|
|
if (4+LGHCTX-1>4)
|
464 |
|
|
assign w_ptable_reg[(4+LGHCTX-1):4] = {
|
465 |
|
|
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX] };
|
466 |
209 |
dgisselq |
if (LGPGSZB > LGLCTX+4)
|
467 |
|
|
assign w_vtable_reg[(LGPGSZB-1):(LGLCTX+4)] = 0;
|
468 |
|
|
if (LGPGSZB > LGHCTX+4)
|
469 |
|
|
assign w_ptable_reg[(LGPGSZB-1):(LGHCTX+4)] = 0;
|
470 |
201 |
dgisselq |
endgenerate
|
471 |
|
|
|
472 |
209 |
dgisselq |
//
|
473 |
201 |
dgisselq |
// Now, reading from the bus
|
474 |
209 |
dgisselq |
/*
|
475 |
|
|
wire [(LGCTXT-1):0] w_ctable_reg;
|
476 |
|
|
assign w_ctable_reg = tlb_cdata[wr_tlb_addr];
|
477 |
|
|
reg setup_this_page_flag;
|
478 |
|
|
reg [(LGCTXT-1):0] setup_page;
|
479 |
|
|
initial setup_this_page_flag = 1'b0;
|
480 |
201 |
dgisselq |
always @(posedge i_clk)
|
481 |
|
|
setup_page <= w_ctable_reg;
|
482 |
|
|
always @(posedge i_clk)
|
483 |
209 |
dgisselq |
setup_this_page_flag <= (!i_reset)&&(i_wbs_cyc_stb)&&(i_wbs_addr[LGTBL+1]);
|
484 |
|
|
*/
|
485 |
201 |
dgisselq |
|
486 |
|
|
|
487 |
|
|
|
488 |
|
|
//////////////////////////////////////////
|
489 |
|
|
//
|
490 |
|
|
//
|
491 |
|
|
// Step two -- handle the page lookup on the master bus
|
492 |
|
|
//
|
493 |
|
|
//
|
494 |
|
|
//////////////////////////////////////////
|
495 |
|
|
|
496 |
|
|
//
|
497 |
|
|
//
|
498 |
|
|
// First clock, and the r_ register, copies the bus data from the bus.
|
499 |
|
|
// While this increases the bus latency, it also gives us a moment to
|
500 |
|
|
// work.
|
501 |
|
|
//
|
502 |
|
|
//
|
503 |
209 |
dgisselq |
wire [(VAW-1):0] r_vpage;
|
504 |
|
|
wire [(PAW-1):0] r_ppage;
|
505 |
|
|
assign r_vpage = (r_addr[(DW-3):(DW-2-VAW)]);
|
506 |
|
|
assign r_ppage = (o_addr[(AW-1):LGPGSZW]);
|
507 |
|
|
|
508 |
|
|
initial s_pending = 1'b0;
|
509 |
201 |
dgisselq |
initial r_pending = 1'b0;
|
510 |
|
|
initial r_valid = 1'b0;
|
511 |
|
|
always @(posedge i_clk)
|
512 |
209 |
dgisselq |
if (i_reset)
|
513 |
201 |
dgisselq |
begin
|
514 |
209 |
dgisselq |
r_pending <= 1'b0;
|
515 |
|
|
r_valid <= 1'b0;
|
516 |
|
|
o_addr <= 0;
|
517 |
|
|
r_we <= 0;
|
518 |
|
|
r_exe <= 0;
|
519 |
|
|
r_addr <= 0;
|
520 |
|
|
r_data <= 0;
|
521 |
|
|
r_sel <= 0;
|
522 |
|
|
//
|
523 |
|
|
s_pending <= 1'b0;
|
524 |
|
|
end else
|
525 |
|
|
begin
|
526 |
201 |
dgisselq |
if (!o_rtn_stall)
|
527 |
|
|
begin
|
528 |
209 |
dgisselq |
r_pending <= (i_wbm_stb)&&(!kernel_context)
|
529 |
|
|
&&(!this_page_valid);
|
530 |
|
|
r_we <= i_wbm_we;
|
531 |
|
|
r_exe <= i_wbm_exe;
|
532 |
|
|
o_addr <= { { (kernel_context)?
|
533 |
|
|
i_wbm_addr[(AW-1):LGPGSZW] : last_ppage },
|
534 |
|
|
i_wbm_addr[(LGPGSZW-1):0] };
|
535 |
|
|
r_addr <= i_wbm_addr;
|
536 |
|
|
r_data <= i_wbm_data;
|
537 |
|
|
r_sel <= i_wbm_sel;
|
538 |
|
|
r_valid <= (i_wbm_stb)&&((kernel_context)||(this_page_valid));
|
539 |
201 |
dgisselq |
s_pending <= 1'b0;
|
540 |
209 |
dgisselq |
end else if (!r_valid) begin
|
541 |
|
|
r_valid <= (pending_page_valid);
|
542 |
|
|
o_addr <= { ppage , r_addr[(LGPGSZW-1):0] };
|
543 |
|
|
r_pending<= (r_pending)&&(!pending_page_valid);
|
544 |
|
|
s_pending <=(r_pending)&&(!pending_page_valid);
|
545 |
201 |
dgisselq |
end else begin
|
546 |
209 |
dgisselq |
r_pending <= 1'b0;
|
547 |
|
|
s_pending <= 1'b0;
|
548 |
201 |
dgisselq |
end
|
549 |
|
|
|
550 |
209 |
dgisselq |
if ((!i_wbm_cyc)||(o_rtn_err)||((o_cyc)&&(i_err)))
|
551 |
|
|
begin
|
552 |
|
|
s_pending <= 1'b0;
|
553 |
201 |
dgisselq |
r_pending <= 1'b0;
|
554 |
209 |
dgisselq |
r_valid <= 1'b0;
|
555 |
|
|
end
|
556 |
201 |
dgisselq |
end
|
557 |
209 |
dgisselq |
|
558 |
|
|
`ifdef FORMAL
|
559 |
|
|
reg f_past_valid;
|
560 |
|
|
|
561 |
|
|
always @(posedge i_clk)
|
562 |
|
|
if ((f_past_valid)&&($past(r_pending))&&(r_pending)&&($past(o_rtn_stall))&&(i_wbm_cyc)&&(!o_stb))
|
563 |
|
|
assert(s_pending);
|
564 |
|
|
`endif
|
565 |
|
|
|
566 |
201 |
dgisselq |
// Second clock: know which buffer entry this belong in.
|
567 |
|
|
// If we don't already know, then the pipeline must be stalled for a
|
568 |
|
|
// while ...
|
569 |
|
|
genvar k, s;
|
570 |
|
|
generate
|
571 |
209 |
dgisselq |
for(k=0; k<TBL_SIZE; k = k + 1)
|
572 |
201 |
dgisselq |
assign r_tlb_match[k] =
|
573 |
209 |
dgisselq |
// The page must be valid
|
574 |
|
|
(tlb_valid[k])
|
575 |
201 |
dgisselq |
// Virtual address must match
|
576 |
209 |
dgisselq |
&&(tlb_vdata[k] == r_vpage)
|
577 |
201 |
dgisselq |
// Context must match as well
|
578 |
209 |
dgisselq |
&&(tlb_cdata[k][LGCTXT-1:1] == r_context_word[LGCTXT-1:1])
|
579 |
|
|
&&((!tlb_cdata[k][0])||(r_context_word[0]));
|
580 |
201 |
dgisselq |
endgenerate
|
581 |
|
|
|
582 |
|
|
initial s_tlb_miss = 1'b0;
|
583 |
|
|
initial s_tlb_hit = 1'b0;
|
584 |
|
|
generate
|
585 |
209 |
dgisselq |
integer i;
|
586 |
201 |
dgisselq |
always @(posedge i_clk)
|
587 |
|
|
begin // valid when s_ becomes valid
|
588 |
|
|
s_tlb_addr <= {(LGTBL){1'b0}};
|
589 |
209 |
dgisselq |
for(i=0; i<TBL_SIZE; i=i+1)
|
590 |
|
|
if (r_tlb_match[i])
|
591 |
|
|
s_tlb_addr <= i[(LGTBL-1):0];
|
592 |
|
|
s_tlb_miss <= (r_pending)&&(r_tlb_match == 0);
|
593 |
201 |
dgisselq |
s_tlb_hit <= 1'b0;
|
594 |
209 |
dgisselq |
for(i=0; i<TBL_SIZE; i=i+1)
|
595 |
|
|
if (r_tlb_match == (1<<i))
|
596 |
|
|
s_tlb_hit <= (r_pending)&&(!r_valid)&&(i_wbm_cyc);
|
597 |
201 |
dgisselq |
end endgenerate
|
598 |
|
|
|
599 |
|
|
// Third clock: Read from the address the virtual table offset,
|
600 |
|
|
// whether read-only, etc.
|
601 |
209 |
dgisselq |
assign s_tlb_flags = tlb_flags[s_tlb_addr];
|
602 |
|
|
assign ro_flag = s_tlb_flags[`ROFLAG];
|
603 |
|
|
assign exe_flag = s_tlb_flags[`EXEFLG];
|
604 |
|
|
assign cachable = s_tlb_flags[`CHFLAG];
|
605 |
201 |
dgisselq |
assign simple_miss = (s_pending)&&(s_tlb_miss);
|
606 |
|
|
assign ro_miss = (s_pending)&&(s_tlb_hit)&&(r_we)&&(ro_flag);
|
607 |
209 |
dgisselq |
assign exe_miss = (s_pending)&&(s_tlb_hit)&&(r_exe)&&(!exe_flag);
|
608 |
201 |
dgisselq |
assign table_err = (s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit);
|
609 |
209 |
dgisselq |
assign vpage = tlb_vdata[s_tlb_addr];
|
610 |
|
|
assign ppage = tlb_pdata[s_tlb_addr];
|
611 |
|
|
|
612 |
|
|
initial pf_cachable = 1'b0;
|
613 |
|
|
always @(posedge i_clk)
|
614 |
|
|
if (i_reset)
|
615 |
|
|
pf_cachable <= 1'b0;
|
616 |
|
|
else
|
617 |
|
|
pf_cachable <= cachable;
|
618 |
|
|
|
619 |
201 |
dgisselq |
initial pf_stb = 1'b0;
|
620 |
209 |
dgisselq |
initial last_ppage = 0;
|
621 |
|
|
initial last_vpage = 0;
|
622 |
201 |
dgisselq |
always @(posedge i_clk)
|
623 |
209 |
dgisselq |
if (i_reset)
|
624 |
201 |
dgisselq |
begin
|
625 |
209 |
dgisselq |
pf_stb <= 1'b0;
|
626 |
|
|
last_ppage <= 0;
|
627 |
|
|
last_vpage <= 0;
|
628 |
|
|
last_tlb <= 0;
|
629 |
|
|
end else if ((!kernel_context)&&(r_pending)&&(!last_page_valid))
|
630 |
|
|
begin
|
631 |
|
|
last_tlb <= s_tlb_addr;
|
632 |
|
|
last_ppage <= ppage;
|
633 |
|
|
last_vpage <= vpage;
|
634 |
|
|
last_exe <= exe_flag;
|
635 |
|
|
last_ro <= ro_flag;
|
636 |
|
|
pf_stb <= 1'b1;
|
637 |
|
|
end else
|
638 |
|
|
pf_stb <= 1'b0;
|
639 |
201 |
dgisselq |
|
640 |
209 |
dgisselq |
initial status_word = 0;
|
641 |
|
|
always @(posedge i_clk)
|
642 |
|
|
if (i_reset)
|
643 |
|
|
status_word <= 0;
|
644 |
|
|
else if (wr_control)
|
645 |
|
|
status_word <= 0;
|
646 |
|
|
else if ((table_err)||(ro_miss)||(simple_miss)||(exe_miss))
|
647 |
|
|
status_word <= { r_vpage,
|
648 |
|
|
{(LGPGSZB-4){1'b0}},
|
649 |
|
|
(table_err), (exe_miss),
|
650 |
|
|
(ro_miss), (simple_miss) };
|
651 |
201 |
dgisselq |
|
652 |
209 |
dgisselq |
initial last_page_valid = 1'b0;
|
653 |
|
|
always @(posedge i_clk)
|
654 |
|
|
if (i_reset)
|
655 |
|
|
last_page_valid <= 1'b0;
|
656 |
|
|
else if ((i_wbs_cyc_stb)&&(i_wbs_we))
|
657 |
|
|
last_page_valid <= 1'b0;
|
658 |
|
|
else if (!kernel_context)
|
659 |
|
|
begin
|
660 |
|
|
if (!o_rtn_stall)
|
661 |
|
|
// A new bus request
|
662 |
|
|
last_page_valid <= (last_page_valid)
|
663 |
|
|
&&(i_wbm_addr[(DW-3):(DW-2-VAW)] == last_vpage);
|
664 |
|
|
else if ((r_pending)&&(!last_page_valid))
|
665 |
|
|
last_page_valid <= (s_pending)&&(s_tlb_hit);
|
666 |
201 |
dgisselq |
end
|
667 |
|
|
|
668 |
209 |
dgisselq |
parameter LGFIFO = 6;
|
669 |
|
|
reg [LGFIFO-1:0] bus_outstanding;
|
670 |
|
|
initial bus_outstanding = 0;
|
671 |
|
|
always @(posedge i_clk)
|
672 |
|
|
if (i_reset)
|
673 |
|
|
bus_outstanding <= 0;
|
674 |
|
|
else if (!o_cyc)
|
675 |
|
|
bus_outstanding <= 0;
|
676 |
|
|
else case({ (o_stb)&&(!i_stall), (i_ack)||(i_err) } )
|
677 |
|
|
2'b01: bus_outstanding <= bus_outstanding - 1'b1;
|
678 |
|
|
2'b10: bus_outstanding <= bus_outstanding + 1'b1;
|
679 |
|
|
default: begin end
|
680 |
|
|
endcase
|
681 |
|
|
|
682 |
|
|
reg bus_pending;
|
683 |
|
|
initial bus_pending = 0;
|
684 |
|
|
always @(posedge i_clk)
|
685 |
|
|
if (i_reset)
|
686 |
|
|
bus_pending <= 0;
|
687 |
|
|
else if (!o_cyc)
|
688 |
|
|
bus_pending <= 1'b0;
|
689 |
|
|
else case({ (o_stb)&&(!i_stall), ((i_ack)||(i_err)) })
|
690 |
|
|
2'b01: bus_pending <= (bus_outstanding > 1);
|
691 |
|
|
2'b10: bus_pending <= 1'b1;
|
692 |
|
|
default: begin end
|
693 |
|
|
endcase
|
694 |
|
|
|
695 |
201 |
dgisselq |
initial rtn_err = 1'b0;
|
696 |
209 |
dgisselq |
initial o_cyc = 1'b0;
|
697 |
201 |
dgisselq |
always @(posedge i_clk)
|
698 |
209 |
dgisselq |
if (i_reset)
|
699 |
201 |
dgisselq |
begin
|
700 |
209 |
dgisselq |
o_cyc <= 1'b0;
|
701 |
|
|
rtn_err <= 1'b0;
|
702 |
|
|
end else begin
|
703 |
|
|
o_cyc <= (i_wbm_cyc)&&(!o_rtn_err)&&((!i_err)||(!o_cyc)); /// &&((o_cyc)||(r_valid));
|
704 |
201 |
dgisselq |
|
705 |
209 |
dgisselq |
rtn_err <= (i_wbm_cyc)&&(i_err)&&(o_cyc);
|
706 |
201 |
dgisselq |
end
|
707 |
209 |
dgisselq |
|
708 |
|
|
generate if (OPT_DELAY_RETURN)
|
709 |
|
|
begin
|
710 |
|
|
reg r_rtn_ack;
|
711 |
|
|
reg [31:0] r_rtn_data;
|
712 |
|
|
|
713 |
|
|
initial r_rtn_data = 0;
|
714 |
|
|
initial r_rtn_ack = 0;
|
715 |
|
|
always @(posedge i_clk)
|
716 |
|
|
if (i_reset)
|
717 |
|
|
begin
|
718 |
|
|
r_rtn_ack <= 0;
|
719 |
|
|
r_rtn_data <= 0;
|
720 |
|
|
end else begin
|
721 |
|
|
r_rtn_ack <= (i_wbm_cyc)&&(i_ack)&&(o_cyc);
|
722 |
|
|
r_rtn_data <= i_data;
|
723 |
|
|
end
|
724 |
|
|
|
725 |
|
|
assign o_rtn_ack = r_rtn_ack;
|
726 |
|
|
assign o_rtn_data = r_rtn_data;
|
727 |
|
|
end else begin
|
728 |
|
|
|
729 |
|
|
assign o_rtn_ack = (i_ack)&&(o_cyc);
|
730 |
|
|
assign o_rtn_data = i_data;
|
731 |
|
|
end endgenerate
|
732 |
|
|
|
733 |
201 |
dgisselq |
assign o_stb = (r_valid);
|
734 |
|
|
assign o_we = (r_we);
|
735 |
209 |
dgisselq |
assign o_rtn_stall = (i_wbm_cyc)&&(
|
736 |
|
|
(o_rtn_err)
|
737 |
|
|
||((r_pending)&&(!r_valid))
|
738 |
|
|
||((o_stb)&&(i_stall))
|
739 |
|
|
||(miss_pending));
|
740 |
201 |
dgisselq |
|
741 |
209 |
dgisselq |
initial miss_pending = 0;
|
742 |
|
|
always @(posedge i_clk)
|
743 |
|
|
if (i_reset)
|
744 |
|
|
miss_pending <= 0;
|
745 |
|
|
else if (!i_wbm_cyc)
|
746 |
|
|
miss_pending <= 0;
|
747 |
|
|
else
|
748 |
|
|
miss_pending <= (i_wbm_cyc)&&(
|
749 |
|
|
(simple_miss)||(ro_miss)||(exe_miss)
|
750 |
|
|
||((s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit)));
|
751 |
|
|
|
752 |
|
|
assign o_rtn_miss = (miss_pending)&&(!bus_pending);
|
753 |
|
|
assign o_rtn_err = (rtn_err);
|
754 |
|
|
|
755 |
|
|
assign o_sel = r_sel;
|
756 |
201 |
dgisselq |
assign o_data = r_data;
|
757 |
|
|
|
758 |
|
|
//
|
759 |
209 |
dgisselq |
assign o_wbs_stall = 1'b0;
|
760 |
|
|
initial o_wbs_ack = 1'b0;
|
761 |
|
|
always @(posedge i_clk)
|
762 |
|
|
if (i_reset)
|
763 |
|
|
o_wbs_ack <= 1'b0;
|
764 |
|
|
else
|
765 |
|
|
o_wbs_ack <= (i_wbs_cyc_stb);
|
766 |
|
|
always @(posedge i_clk)
|
767 |
|
|
if (i_reset)
|
768 |
|
|
o_wbs_data <= 0;
|
769 |
|
|
else case({i_wbs_addr[LGTBL+1],i_wbs_addr[0]})
|
770 |
|
|
2'b00: o_wbs_data <= w_control_data;
|
771 |
|
|
2'b01: o_wbs_data <= status_word;
|
772 |
|
|
2'b10: o_wbs_data <= w_vtable_reg;
|
773 |
|
|
2'b11: o_wbs_data <= w_ptable_reg;
|
774 |
|
|
endcase
|
775 |
|
|
|
776 |
|
|
//
|
777 |
201 |
dgisselq |
// Bus snooping returns ...
|
778 |
|
|
//
|
779 |
|
|
assign pf_return_stb = pf_stb;
|
780 |
|
|
assign pf_return_we = r_we;
|
781 |
|
|
assign pf_return_p = last_ppage;
|
782 |
|
|
assign pf_return_v = last_vpage;
|
783 |
|
|
assign pf_return_cachable = pf_cachable;
|
784 |
|
|
|
785 |
209 |
dgisselq |
// Also requires being told when/if the page changed
|
786 |
|
|
// So, on a page change,
|
787 |
|
|
// pf_return_we = 1
|
788 |
|
|
// pf_stb = 1
|
789 |
|
|
// and pf_return_p has the physical address
|
790 |
|
|
|
791 |
|
|
// Make verilator happy
|
792 |
|
|
// verilator lint_off UNUSED
|
793 |
|
|
wire [(PAW-1):0] unused;
|
794 |
|
|
assign unused = r_ppage;
|
795 |
|
|
generate if (4+LGCTXT < LGPGSZB)
|
796 |
|
|
begin
|
797 |
|
|
wire [LGPGSZB-(4+LGCTXT)-1:0] unused_data;
|
798 |
|
|
assign unused_data = i_wbs_data[LGPGSZB-1:4+LGCTXT];
|
799 |
|
|
end endgenerate
|
800 |
|
|
|
801 |
|
|
wire unused_always;
|
802 |
|
|
assign unused_always = s_tlb_flags[0];
|
803 |
|
|
// verilator lint_on UNUSED
|
804 |
|
|
|
805 |
|
|
`ifdef FORMAL
|
806 |
|
|
initial f_past_valid = 0;
|
807 |
|
|
always @(posedge i_clk)
|
808 |
|
|
f_past_valid <= 1'b1;
|
809 |
|
|
|
810 |
|
|
initial assume(i_reset);
|
811 |
|
|
always @(*)
|
812 |
|
|
if (!f_past_valid)
|
813 |
|
|
assume(i_reset);
|
814 |
|
|
|
815 |
|
|
always @(*)
|
816 |
|
|
if (i_reset)
|
817 |
|
|
assume(!i_wbs_cyc_stb);
|
818 |
|
|
always @(posedge i_clk)
|
819 |
|
|
if (f_past_valid)
|
820 |
|
|
assert(o_wbs_ack == $past(i_wbs_cyc_stb));
|
821 |
|
|
always @(*)
|
822 |
|
|
assert(o_wbs_stall == 1'b0);
|
823 |
|
|
|
824 |
|
|
always @(*)
|
825 |
|
|
assume((!i_wbm_cyc)||(!i_wbs_cyc_stb));
|
826 |
|
|
|
827 |
|
|
localparam F_LGDEPTH = 6;
|
828 |
|
|
reg [F_LGDEPTH-1:0] fv_nreqs, fv_nacks, fv_outstanding,
|
829 |
|
|
fp_nreqs, fp_nacks, fp_outstanding;
|
830 |
|
|
|
831 |
|
|
localparam F_MAX_STALL = 3,
|
832 |
|
|
F_MAX_WAIT = 2,
|
833 |
|
|
F_MAX_REQ = 9;
|
834 |
|
|
|
835 |
|
|
//
|
836 |
|
|
// The stall period needs to be long enough to allow all in-progress
|
837 |
|
|
// transactions to complete, as in the case of a page miss. Hence,
|
838 |
|
|
// the max stall amount depends upon the max wait time for the
|
839 |
|
|
// physical half of the interaction. It is artificially limited here
|
840 |
|
|
// in order to limit the amount of proof time required.
|
841 |
|
|
//
|
842 |
|
|
fwb_slave #(.F_MAX_STALL(F_MAX_STALL+(F_MAX_WAIT*F_MAX_REQ)+2),
|
843 |
|
|
.AW(DW-2),
|
844 |
|
|
.F_MAX_ACK_DELAY(F_MAX_STALL+F_MAX_WAIT+5),
|
845 |
|
|
.F_MAX_REQUESTS(F_MAX_REQ),
|
846 |
|
|
.F_LGDEPTH(F_LGDEPTH),
|
847 |
|
|
.F_OPT_MINCLOCK_DELAY(0))
|
848 |
|
|
busslave(i_clk, i_reset,
|
849 |
|
|
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_addr,
|
850 |
|
|
i_wbm_data, i_wbm_sel,
|
851 |
|
|
o_rtn_ack, o_rtn_stall, o_rtn_data,
|
852 |
|
|
o_rtn_err|o_rtn_miss,
|
853 |
|
|
fv_nreqs, fv_nacks, fv_outstanding);
|
854 |
|
|
|
855 |
|
|
fwb_master #(.F_MAX_STALL(F_MAX_STALL),
|
856 |
|
|
.AW(ADDRESS_WIDTH),
|
857 |
|
|
.F_MAX_ACK_DELAY(F_MAX_WAIT),
|
858 |
|
|
.F_MAX_REQUESTS(F_MAX_REQ),
|
859 |
|
|
.F_LGDEPTH(F_LGDEPTH),
|
860 |
|
|
.F_OPT_MINCLOCK_DELAY(0))
|
861 |
|
|
busmaster(i_clk, i_reset,
|
862 |
|
|
o_cyc, o_stb, o_we, o_addr,
|
863 |
|
|
o_data, o_sel,
|
864 |
|
|
i_ack, i_stall, i_data, i_err,
|
865 |
|
|
fp_nreqs, fp_nacks, fp_outstanding);
|
866 |
|
|
|
867 |
|
|
always @(*)
|
868 |
|
|
assert((!o_cyc)||(fp_outstanding == bus_outstanding));
|
869 |
|
|
|
870 |
|
|
always @(*)
|
871 |
|
|
assume(fv_nreqs < F_MAX_REQ);
|
872 |
|
|
always @(*)
|
873 |
|
|
if ((i_wbm_cyc)&&(o_cyc)&&(fv_outstanding == fp_outstanding))
|
874 |
|
|
assert(fv_nreqs == fp_nreqs);
|
875 |
|
|
always @(*)
|
876 |
|
|
if ((i_wbm_cyc)&&(o_cyc))
|
877 |
|
|
begin
|
878 |
|
|
assert(fp_nreqs <= fv_nreqs);
|
879 |
|
|
assert(fp_nacks >= fv_nacks);
|
880 |
|
|
end
|
881 |
|
|
|
882 |
|
|
reg [F_LGDEPTH-1:0] f_expected, f_ex_nreqs, f_ex_nacks;
|
883 |
|
|
always @(*)
|
884 |
|
|
if (!i_wbm_cyc)
|
885 |
|
|
begin
|
886 |
|
|
f_ex_nreqs <= 0;
|
887 |
|
|
f_ex_nacks <= 0;
|
888 |
|
|
f_expected <= 0;
|
889 |
|
|
end else if (OPT_DELAY_RETURN)
|
890 |
|
|
begin
|
891 |
|
|
if (r_pending)
|
892 |
|
|
begin
|
893 |
|
|
f_ex_nreqs <= fp_nreqs + 1'b1;
|
894 |
|
|
f_ex_nacks <= fp_nacks + o_rtn_ack;
|
895 |
|
|
f_expected <= fp_outstanding + 1'b1
|
896 |
|
|
+ o_rtn_ack;
|
897 |
|
|
end else begin
|
898 |
|
|
f_expected <= fp_outstanding + (o_stb)
|
899 |
|
|
+ (o_rtn_ack);
|
900 |
|
|
f_ex_nreqs <= fp_nreqs + o_stb;
|
901 |
|
|
f_ex_nacks <= fp_nacks + o_rtn_ack;
|
902 |
|
|
end
|
903 |
|
|
end else begin
|
904 |
|
|
if (r_pending)
|
905 |
|
|
begin
|
906 |
|
|
f_ex_nreqs <= fp_nreqs + 1'b1;
|
907 |
|
|
f_ex_nacks <= fp_nacks;
|
908 |
|
|
f_expected <= fp_outstanding + 1'b1;
|
909 |
|
|
end else begin
|
910 |
|
|
f_ex_nreqs <= fp_nreqs + o_stb;
|
911 |
|
|
f_ex_nacks <= fp_nacks;
|
912 |
|
|
f_expected <= fp_outstanding + (o_stb);
|
913 |
|
|
end
|
914 |
|
|
end
|
915 |
|
|
|
916 |
|
|
reg f_kill_input;
|
917 |
|
|
initial f_kill_input = 1'b0;
|
918 |
|
|
always @(posedge i_clk)
|
919 |
|
|
f_kill_input <= (i_wbm_cyc)&&(
|
920 |
|
|
(i_reset)
|
921 |
|
|
||(o_rtn_miss)
|
922 |
|
|
||(o_rtn_err));
|
923 |
|
|
always @(*)
|
924 |
|
|
if (f_kill_input)
|
925 |
|
|
assume(!i_wbm_cyc);
|
926 |
|
|
|
927 |
|
|
always @(posedge i_clk)
|
928 |
|
|
if ((f_past_valid)&&($past(o_rtn_miss))&&($past(i_wbm_cyc)))
|
929 |
|
|
begin
|
930 |
|
|
assume(!o_cyc);
|
931 |
|
|
assume(!i_wbm_cyc);
|
932 |
|
|
end
|
933 |
|
|
|
934 |
|
|
wire fv_is_one, fp_is_zero;
|
935 |
|
|
assign fv_is_one = (fv_outstanding == 1);
|
936 |
|
|
assign fp_is_zero = (fp_outstanding == 0);
|
937 |
|
|
always @(*)
|
938 |
|
|
if ((i_wbm_cyc)&&(o_cyc))
|
939 |
|
|
begin
|
940 |
|
|
if (o_rtn_miss)
|
941 |
|
|
begin
|
942 |
|
|
assert(fp_outstanding == 0);
|
943 |
|
|
assert(fv_outstanding == 1);
|
944 |
|
|
assert(fv_is_one);
|
945 |
|
|
assert(fp_is_zero);
|
946 |
|
|
end else begin
|
947 |
|
|
assert(fv_nreqs == f_ex_nreqs);
|
948 |
|
|
assert(fv_nacks == f_ex_nacks);
|
949 |
|
|
assert(fv_outstanding >= fp_outstanding);
|
950 |
|
|
assert(fv_outstanding == f_expected);
|
951 |
|
|
end
|
952 |
|
|
end
|
953 |
|
|
|
954 |
|
|
always @(*)
|
955 |
|
|
assert(z_context == (r_context_word == 0));
|
956 |
|
|
always @(*)
|
957 |
|
|
assert(kernel_context == ( ((r_context_word == 0)||(!i_gie)) ? 1'b1 : 1'b0));
|
958 |
|
|
always @(posedge i_clk)
|
959 |
|
|
if ((f_past_valid)&&($past(i_wbs_cyc_stb)))
|
960 |
|
|
assume(!i_wbm_cyc);
|
961 |
|
|
always @(*)
|
962 |
|
|
if (o_wbs_ack)
|
963 |
|
|
assume(!i_wbm_cyc);
|
964 |
|
|
|
965 |
|
|
always @(*)
|
966 |
|
|
assert((!i_wbm_cyc)||(!o_wbs_ack));
|
967 |
|
|
always @(posedge i_clk)
|
968 |
|
|
if ((f_past_valid)&&(r_pending)&&($past(kernel_context))
|
969 |
|
|
&&($past(i_wbm_stb))&&(!$past(i_stall))&&(i_wbm_cyc)
|
970 |
|
|
&&(!o_rtn_stall))
|
971 |
|
|
assert(o_addr[(AW-1):0] == $past(i_wbm_addr[(AW-1):0]));
|
972 |
|
|
always @(*)
|
973 |
|
|
assert(bus_pending == (bus_outstanding > 0));
|
974 |
|
|
|
975 |
|
|
always @(*)
|
976 |
|
|
if ((s_pending)&&(!s_tlb_miss))
|
977 |
|
|
assert(r_tlb_match[s_tlb_addr]);
|
978 |
|
|
|
979 |
|
|
// Check out all of the criteria which should clear these flags
|
980 |
|
|
always @(posedge i_clk)
|
981 |
|
|
if ((f_past_valid)&&(($past(i_reset))
|
982 |
|
|
||(!$past(i_wbm_cyc))
|
983 |
|
|
||(!$past(o_rtn_stall))))
|
984 |
|
|
begin
|
985 |
|
|
assert(!simple_miss);
|
986 |
|
|
assert(!ro_miss);
|
987 |
|
|
assert(!exe_miss);
|
988 |
|
|
assert(!table_err);
|
989 |
|
|
if (!$past(i_wbm_we))
|
990 |
|
|
assert(!ro_miss);
|
991 |
|
|
|
992 |
|
|
if (!kernel_context)
|
993 |
|
|
begin
|
994 |
|
|
assert((!o_stb)||(!(simple_miss|ro_miss|table_err)));
|
995 |
|
|
// This doesn't belong on the clear list, but on the
|
996 |
|
|
// should be set list
|
997 |
|
|
// assert((!o_stb)||(!s_tlb_hit));
|
998 |
|
|
end
|
999 |
|
|
end
|
1000 |
|
|
|
1001 |
|
|
always @(posedge i_clk)
|
1002 |
|
|
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
|
1003 |
|
|
&&(!$past(o_rtn_stall)))
|
1004 |
|
|
begin
|
1005 |
|
|
if ((!$past(kernel_context))&&(o_stb))
|
1006 |
|
|
assert((last_page_valid)||(s_tlb_hit));
|
1007 |
|
|
end
|
1008 |
|
|
|
1009 |
|
|
reg [(LGTBL-1):0] f_last_page;
|
1010 |
|
|
always @(posedge i_clk)
|
1011 |
|
|
if ((f_past_valid)&&(!kernel_context)&&(r_pending)&&(!last_page_valid))
|
1012 |
|
|
f_last_page <= s_tlb_addr;
|
1013 |
|
|
|
1014 |
|
|
wire [3:0] tlb_flag_last_page;
|
1015 |
|
|
assign tlb_flag_last_page = tlb_flags[f_last_page];
|
1016 |
|
|
always @(*)
|
1017 |
|
|
if (last_page_valid)
|
1018 |
|
|
begin
|
1019 |
|
|
assert(tlb_valid[f_last_page]);
|
1020 |
|
|
assert(last_tlb == f_last_page);
|
1021 |
|
|
assert(last_ppage == tlb_pdata[f_last_page]);
|
1022 |
|
|
assert(last_vpage == tlb_vdata[f_last_page]);
|
1023 |
|
|
assert(last_ro == tlb_flag_last_page[`ROFLAG]);
|
1024 |
|
|
assert(last_exe == tlb_flag_last_page[`EXEFLG]);
|
1025 |
|
|
assert(r_context_word[LGCTXT-1:1] == tlb_cdata[f_last_page][LGCTXT-1:1]);
|
1026 |
|
|
if (!r_context_word[0])
|
1027 |
|
|
assert(!tlb_cdata[f_last_page][0]);
|
1028 |
|
|
assert((!r_context_word[0])||(r_context_word[0]));
|
1029 |
|
|
end
|
1030 |
|
|
|
1031 |
|
|
always @(posedge i_clk)
|
1032 |
|
|
if ((f_past_valid)&&(!$past(i_reset))
|
1033 |
|
|
&&($past(last_page_valid))&&(!$past(kernel_context))
|
1034 |
|
|
&&($past(o_stb))&&($past(i_wbm_cyc)))
|
1035 |
|
|
assert(tlb_accessed[$past(last_tlb)]);
|
1036 |
|
|
|
1037 |
|
|
always @(posedge i_clk)
|
1038 |
|
|
if ((f_past_valid)&&(!$past(i_reset))
|
1039 |
|
|
&&($past(pending_page_valid))&&(!$past(kernel_context))
|
1040 |
|
|
&&($past(o_stb))&&($past(i_wbm_cyc)))
|
1041 |
|
|
assert(tlb_accessed[$past(s_tlb_addr)]);
|
1042 |
|
|
|
1043 |
|
|
always @(posedge i_clk)
|
1044 |
|
|
if ((f_past_valid)&&(!$past(kernel_context))&&(o_stb))
|
1045 |
|
|
begin
|
1046 |
|
|
assert(last_page_valid);
|
1047 |
|
|
assert(r_ppage == last_ppage);
|
1048 |
|
|
assert((!last_ro)||(!o_we));
|
1049 |
|
|
end
|
1050 |
|
|
|
1051 |
|
|
always @(posedge i_clk)
|
1052 |
|
|
if ((f_past_valid)&&($past(o_stb))&&(o_stb)&&(i_wbm_cyc))
|
1053 |
|
|
assert((last_page_valid)||(kernel_context));
|
1054 |
|
|
|
1055 |
|
|
always @(*)
|
1056 |
|
|
assert((!s_tlb_hit)||(!s_tlb_miss));
|
1057 |
|
|
// always @(*)
|
1058 |
|
|
// if ((fp_outstanding > 0)&&(o_cyc)&&(!o_stb)&&(!r_pending)&&(!kernel_context))
|
1059 |
|
|
// assert(last_page_valid);
|
1060 |
|
|
// always @(*) assume(kernel_context);
|
1061 |
|
|
always @(*)
|
1062 |
|
|
assume((!i_wbs_cyc_stb)||(!i_gie));
|
1063 |
|
|
|
1064 |
|
|
reg f_past_gie, f_past_wbm_cyc;
|
1065 |
|
|
|
1066 |
|
|
initial f_past_gie = 1'b0;
|
1067 |
|
|
always @(posedge i_clk)
|
1068 |
|
|
f_past_gie <= i_gie;
|
1069 |
|
|
|
1070 |
|
|
initial f_past_wbm_cyc = 1'b0;
|
1071 |
|
|
always @(posedge i_clk)
|
1072 |
|
|
f_past_wbm_cyc <= i_wbm_cyc;
|
1073 |
|
|
always @(*)
|
1074 |
|
|
if ((f_past_valid)&&(bus_pending))
|
1075 |
|
|
assume(i_gie == f_past_gie);
|
1076 |
|
|
always @(*)
|
1077 |
|
|
if ((f_past_wbm_cyc)&&(i_wbm_cyc))
|
1078 |
|
|
assume(i_gie == f_past_gie);
|
1079 |
|
|
|
1080 |
|
|
always @(posedge i_clk)
|
1081 |
|
|
if ((f_past_valid)&&(i_wbm_cyc)&&($past(i_wbm_cyc)))
|
1082 |
|
|
assume(i_gie == $past(i_gie));
|
1083 |
|
|
always @(posedge i_clk)
|
1084 |
|
|
if ((f_past_valid)&&($past(i_reset)))
|
1085 |
|
|
assume(!i_gie);
|
1086 |
|
|
|
1087 |
|
|
always @(posedge i_clk)
|
1088 |
|
|
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
|
1089 |
|
|
&&($past(!kernel_context))
|
1090 |
|
|
&&($past(r_pending))
|
1091 |
|
|
&&(!$past(last_page_valid)))
|
1092 |
|
|
begin
|
1093 |
|
|
if (($past(s_tlb_hit))
|
1094 |
|
|
&&(!$past(ro_miss))
|
1095 |
|
|
&&(!$past(exe_miss)))
|
1096 |
|
|
begin
|
1097 |
|
|
assert(last_vpage == $past(r_vpage));
|
1098 |
|
|
assert(last_page_valid);
|
1099 |
|
|
assert(!miss_pending);
|
1100 |
|
|
assert(tlb_accessed[s_tlb_addr]);
|
1101 |
|
|
end else if (($past(s_tlb_hit))&&($past(ro_miss)))
|
1102 |
|
|
begin
|
1103 |
|
|
assert(miss_pending);
|
1104 |
|
|
assert(last_page_valid);
|
1105 |
|
|
assert(status_word[3:0] == 4'h2);
|
1106 |
|
|
end else if (($past(s_tlb_hit))&&($past(exe_miss)))
|
1107 |
|
|
begin
|
1108 |
|
|
assert(miss_pending);
|
1109 |
|
|
assert(last_page_valid);
|
1110 |
|
|
assert(status_word[3:0] == 4'h4);
|
1111 |
|
|
end else if (($past(s_tlb_hit))&&($past(simple_miss)))
|
1112 |
|
|
begin
|
1113 |
|
|
assert(miss_pending);
|
1114 |
|
|
assert(last_page_valid);
|
1115 |
|
|
assert(status_word[3:0] == 4'h1);
|
1116 |
|
|
end else if (!$past(s_tlb_hit))
|
1117 |
|
|
begin
|
1118 |
|
|
assert(!last_page_valid);
|
1119 |
|
|
end
|
1120 |
|
|
end
|
1121 |
|
|
|
1122 |
|
|
always @(*)
|
1123 |
|
|
assert((!ro_miss)||(!exe_miss)||(!simple_miss)||(!table_err));
|
1124 |
|
|
|
1125 |
|
|
reg [4:0] f_tlb_pipe;
|
1126 |
|
|
|
1127 |
|
|
initial f_tlb_pipe = 5'h0;
|
1128 |
|
|
always @(posedge i_clk)
|
1129 |
|
|
if (i_reset)
|
1130 |
|
|
f_tlb_pipe <= 5'h0;
|
1131 |
|
|
else if ((!r_pending)||(o_stb))
|
1132 |
|
|
f_tlb_pipe <= 5'h0;
|
1133 |
|
|
else if ((r_pending)&&(!r_valid)&&(!miss_pending))
|
1134 |
|
|
f_tlb_pipe <= { f_tlb_pipe[3:0], 1'b1 };
|
1135 |
|
|
|
1136 |
|
|
always @(*)
|
1137 |
|
|
assert(f_tlb_pipe != 5'h1f);
|
1138 |
|
|
|
1139 |
|
|
always @(*) // WE or EXE, never both
|
1140 |
|
|
assume((!i_wbm_stb)||(!i_wbm_we)||(!i_wbm_exe));
|
1141 |
|
|
always @(posedge i_clk)
|
1142 |
|
|
if ((f_past_valid)&&($past(i_wbm_stb))&&($past(o_rtn_stall)))
|
1143 |
|
|
assume(i_wbm_exe == $past(i_wbm_exe));
|
1144 |
|
|
|
1145 |
|
|
always @(*)
|
1146 |
|
|
assert((!r_pending)||(!o_stb));
|
1147 |
|
|
always @(*)
|
1148 |
|
|
assert((!s_pending)||(!o_stb));
|
1149 |
|
|
always @(*)
|
1150 |
|
|
assert((!s_pending)||(r_pending));
|
1151 |
|
|
always @(posedge i_clk)
|
1152 |
|
|
if ((f_past_valid)&&($past(i_wbm_cyc)))
|
1153 |
|
|
assume(!i_wbs_cyc_stb);
|
1154 |
|
|
|
1155 |
|
|
always @(posedge i_clk)
|
1156 |
|
|
if ((f_past_valid)&&(|status_word[3:0])&&(!$past(i_wbm_cyc)))
|
1157 |
|
|
assume(!i_gie);
|
1158 |
|
|
`endif
|
1159 |
201 |
dgisselq |
endmodule
|