1 |
69 |
dgisselq |
////////////////////////////////////////////////////////////////////////////////
|
2 |
|
|
//
|
3 |
82 |
dgisselq |
// Filename: pfcache.v
|
4 |
69 |
dgisselq |
//
|
5 |
|
|
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
|
6 |
|
|
//
|
7 |
|
|
// Purpose: Keeping our CPU fed with instructions, at one per clock and
|
8 |
209 |
dgisselq |
// with only a minimum number stalls. The entire cache may also
|
9 |
|
|
// be cleared (if necessary).
|
10 |
69 |
dgisselq |
//
|
11 |
209 |
dgisselq |
// This logic is driven by a couple realities:
|
12 |
|
|
// 1. It takes a clock to read from a block RAM address, and hence a clock
|
13 |
|
|
// to read from the cache.
|
14 |
|
|
// 2. It takes another clock to check that the tag matches
|
15 |
|
|
//
|
16 |
|
|
// Our goal will be to avoid this second check if at all possible.
|
17 |
|
|
// Hence, we'll test on the clock of any given request whether
|
18 |
|
|
// or not the request matches the last tag value, and on the next
|
19 |
|
|
// clock whether it new tag value (if it has changed). Hence,
|
20 |
|
|
// for anything found within the cache, there will be a one
|
21 |
|
|
// cycle delay on any branch.
|
22 |
|
|
//
|
23 |
|
|
//
|
24 |
|
|
// Address Words are separated into three components:
|
25 |
|
|
// [ Tag bits ] [ Cache line number ] [ Cache position w/in the line ]
|
26 |
|
|
//
|
27 |
|
|
// On any read from the cache, only the second two components are required.
|
28 |
|
|
// On any read from memory, the first two components will be fixed across
|
29 |
|
|
// the bus, and the third component will be adjusted from zero to its
|
30 |
|
|
// maximum value.
|
31 |
|
|
//
|
32 |
|
|
//
|
33 |
69 |
dgisselq |
// Creator: Dan Gisselquist, Ph.D.
|
34 |
|
|
// Gisselquist Technology, LLC
|
35 |
|
|
//
|
36 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
37 |
|
|
//
|
38 |
209 |
dgisselq |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
|
39 |
69 |
dgisselq |
//
|
40 |
|
|
// This program is free software (firmware): you can redistribute it and/or
|
41 |
|
|
// modify it under the terms of the GNU General Public License as published
|
42 |
|
|
// by the Free Software Foundation, either version 3 of the License, or (at
|
43 |
|
|
// your option) any later version.
|
44 |
|
|
//
|
45 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
46 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
|
47 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
48 |
|
|
// for more details.
|
49 |
|
|
//
|
50 |
201 |
dgisselq |
// You should have received a copy of the GNU General Public License along
|
51 |
|
|
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
|
52 |
|
|
// target there if the PDF file isn't present.) If not, see
|
53 |
|
|
// <http://www.gnu.org/licenses/> for a copy.
|
54 |
|
|
//
|
55 |
69 |
dgisselq |
// License: GPL, v3, as defined and found on www.gnu.org,
|
56 |
|
|
// http://www.gnu.org/licenses/gpl.html
|
57 |
|
|
//
|
58 |
|
|
//
|
59 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
60 |
|
|
//
|
61 |
201 |
dgisselq |
//
|
62 |
209 |
dgisselq |
`default_nettype none
|
63 |
|
|
//
|
64 |
|
|
module pfcache(i_clk, i_reset, i_new_pc, i_clear_cache,
|
65 |
69 |
dgisselq |
// i_early_branch, i_from_addr,
|
66 |
209 |
dgisselq |
i_stall_n, i_pc, o_insn, o_pc, o_valid,
|
67 |
69 |
dgisselq |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
|
68 |
|
|
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data,
|
69 |
209 |
dgisselq |
o_illegal
|
70 |
|
|
`ifdef NOT_YET_READY
|
71 |
|
|
, i_mmu_ack, i_mmu_we, i_mmu_paddr
|
72 |
|
|
`endif
|
73 |
|
|
`ifdef FORMAL
|
74 |
|
|
, f_pc_wb
|
75 |
|
|
`endif
|
76 |
|
|
);
|
77 |
|
|
`ifdef FORMAL
|
78 |
|
|
parameter LGCACHELEN = 4, ADDRESS_WIDTH=30,
|
79 |
|
|
LGLINES=2; // Log of the number of separate cache lines
|
80 |
|
|
`else
|
81 |
|
|
parameter LGCACHELEN = 12, ADDRESS_WIDTH=30,
|
82 |
|
|
LGLINES=6; // Log of the number of separate cache lines
|
83 |
|
|
`endif
|
84 |
|
|
localparam CACHELEN=(1<<LGCACHELEN); //Wrd Size of our cache memory
|
85 |
194 |
dgisselq |
localparam CW=LGCACHELEN; // Short hand for LGCACHELEN
|
86 |
209 |
dgisselq |
localparam LS=LGCACHELEN-LGLINES; // Size of a cache line
|
87 |
194 |
dgisselq |
localparam BUSW = 32; // Number of data lines on the bus
|
88 |
|
|
localparam AW=ADDRESS_WIDTH; // Shorthand for ADDRESS_WIDTH
|
89 |
69 |
dgisselq |
//
|
90 |
209 |
dgisselq |
input wire i_clk, i_reset;
|
91 |
|
|
//
|
92 |
|
|
// The interface with the rest of the CPU
|
93 |
|
|
input wire i_new_pc;
|
94 |
|
|
input wire i_clear_cache;
|
95 |
|
|
input wire i_stall_n;
|
96 |
|
|
input wire [(AW+1):0] i_pc;
|
97 |
|
|
output wire [(BUSW-1):0] o_insn;
|
98 |
|
|
output wire [(AW+1):0] o_pc;
|
99 |
|
|
output wire o_valid;
|
100 |
|
|
//
|
101 |
|
|
// The wishbone bus interface
|
102 |
|
|
output reg o_wb_cyc, o_wb_stb;
|
103 |
|
|
output wire o_wb_we;
|
104 |
69 |
dgisselq |
output reg [(AW-1):0] o_wb_addr;
|
105 |
|
|
output wire [(BUSW-1):0] o_wb_data;
|
106 |
|
|
//
|
107 |
209 |
dgisselq |
input wire i_wb_ack, i_wb_stall, i_wb_err;
|
108 |
|
|
input wire [(BUSW-1):0] i_wb_data;
|
109 |
69 |
dgisselq |
//
|
110 |
209 |
dgisselq |
// o_illegal will be true if this instruction was the result of a
|
111 |
|
|
// bus error (This is also part of the CPU interface)
|
112 |
69 |
dgisselq |
output reg o_illegal;
|
113 |
209 |
dgisselq |
//
|
114 |
|
|
`ifdef NOT_YET_READY
|
115 |
|
|
input wire i_mmu_ack, i_mmu_we;
|
116 |
|
|
input wire [(PAW-1):0] i_mmu_paddr;
|
117 |
|
|
`endif
|
118 |
69 |
dgisselq |
|
119 |
|
|
// Fixed bus outputs: we read from the bus only, never write.
|
120 |
|
|
// Thus the output data is ... irrelevant and don't care. We set it
|
121 |
|
|
// to zero just to set it to something.
|
122 |
|
|
assign o_wb_we = 1'b0;
|
123 |
|
|
assign o_wb_data = 0;
|
124 |
|
|
|
125 |
209 |
dgisselq |
`ifdef NOT_YET_READY
|
126 |
|
|
// These wires will be used below as part of the cache invalidation
|
127 |
|
|
// routine, should the MMU be used. This allows us to snoop on the
|
128 |
|
|
// physical side of the MMU bus, and invalidate any results should
|
129 |
|
|
// we need to do so.
|
130 |
|
|
wire mmu_inval;
|
131 |
|
|
wire [(PAW-CW-1):0] mmu_mskaddr;
|
132 |
|
|
`endif
|
133 |
|
|
`ifdef FORMAL
|
134 |
|
|
output wire [AW-1:0] f_pc_wb;
|
135 |
|
|
assign f_pc_wb = i_pc[AW+1:2];
|
136 |
|
|
`endif
|
137 |
|
|
|
138 |
|
|
|
139 |
176 |
dgisselq |
wire r_v;
|
140 |
69 |
dgisselq |
reg [(BUSW-1):0] cache [0:((1<<CW)-1)];
|
141 |
209 |
dgisselq |
reg [(AW-CW-1):0] cache_tags [0:((1<<(LGLINES))-1)];
|
142 |
|
|
reg [((1<<(LGLINES))-1):0] valid_mask;
|
143 |
69 |
dgisselq |
|
144 |
209 |
dgisselq |
reg r_v_from_pc, r_v_from_last, r_new_request;
|
145 |
|
|
reg rvsrc;
|
146 |
|
|
wire w_v_from_pc, w_v_from_last;
|
147 |
|
|
reg [(AW+1):0] lastpc;
|
148 |
|
|
reg [(CW-1):0] wraddr;
|
149 |
176 |
dgisselq |
reg [(AW-1):CW] tagvalipc, tagvallst;
|
150 |
|
|
wire [(AW-1):CW] tagval;
|
151 |
209 |
dgisselq |
wire [(AW-1):LS] lasttag;
|
152 |
129 |
dgisselq |
reg illegal_valid;
|
153 |
209 |
dgisselq |
reg [(AW-1):LS] illegal_cache;
|
154 |
69 |
dgisselq |
|
155 |
176 |
dgisselq |
// initial o_i = 32'h76_00_00_00; // A NOOP instruction
|
156 |
|
|
// initial o_pc = 0;
|
157 |
|
|
reg [(BUSW-1):0] r_pc_cache, r_last_cache;
|
158 |
209 |
dgisselq |
reg [(AW+1):0] r_pc, r_lastpc;
|
159 |
|
|
reg isrc;
|
160 |
|
|
reg [1:0] delay;
|
161 |
|
|
reg svmask, last_ack, needload, last_addr,
|
162 |
|
|
bus_abort;
|
163 |
|
|
reg [(LGLINES-1):0] saddr;
|
164 |
|
|
|
165 |
|
|
wire w_advance;
|
166 |
|
|
assign w_advance = (i_new_pc)||((r_v)&&(i_stall_n));
|
167 |
|
|
|
168 |
|
|
/////////////////////////////////////////////////
|
169 |
|
|
//
|
170 |
|
|
// Read the instruction from the cache
|
171 |
|
|
//
|
172 |
|
|
/////////////////////////////////////////////////
|
173 |
|
|
//
|
174 |
|
|
//
|
175 |
|
|
// We'll read two values from the cache, the first is the value if
|
176 |
|
|
// i_pc contains the address we want, the second is the value we'd read
|
177 |
|
|
// if lastpc (i.e. $past(i_pc)) was the address we wanted.
|
178 |
|
|
initial r_pc = 0;
|
179 |
|
|
initial r_lastpc = 0;
|
180 |
69 |
dgisselq |
always @(posedge i_clk)
|
181 |
176 |
dgisselq |
begin
|
182 |
|
|
// We don't have the logic to select what to read, we must
|
183 |
|
|
// read both the value at i_pc and lastpc. cache[i_pc] is
|
184 |
209 |
dgisselq |
// the value we return if the last cache request was in the
|
185 |
|
|
// cache on the last clock, cacne[lastpc] is the value we
|
186 |
|
|
// return if we've been stalled, weren't valid, or had to wait
|
187 |
|
|
// a clock or two.
|
188 |
176 |
dgisselq |
//
|
189 |
209 |
dgisselq |
// Part of the issue here is that i_pc is going to increment
|
190 |
|
|
// on this clock before we know whether or not the cache entry
|
191 |
|
|
// we've just read is valid. We can't stop this. Hence, we
|
192 |
|
|
// need to read from the lastpc entry.
|
193 |
|
|
//
|
194 |
|
|
//
|
195 |
|
|
// Here we keep track of which answer we want/need.
|
196 |
|
|
// If we reported a valid value to the CPU on the last clock,
|
197 |
|
|
// and the CPU wasn't stalled, then we want to use i_pc.
|
198 |
|
|
// Likewise if the CPU gave us an i_new_pc request, then we'll
|
199 |
|
|
// want to return the value associated with reading the cache
|
200 |
|
|
// at i_pc.
|
201 |
|
|
isrc <= w_advance;
|
202 |
69 |
dgisselq |
|
203 |
209 |
dgisselq |
// Here we read both cache entries, at i_pc and lastpc.
|
204 |
|
|
// We'll select from among these cache possibilities on the
|
205 |
|
|
// next clock
|
206 |
|
|
r_pc_cache <= cache[i_pc[(CW+1):2]];
|
207 |
|
|
r_last_cache <= cache[lastpc[(CW+1):2]];
|
208 |
|
|
//
|
209 |
|
|
// Let's also register(delay) the r_pc and r_lastpc values
|
210 |
|
|
// for the next clock, so we can accurately report the address
|
211 |
|
|
// of the cache value we just looked up.
|
212 |
176 |
dgisselq |
r_pc <= i_pc;
|
213 |
|
|
r_lastpc <= lastpc;
|
214 |
|
|
end
|
215 |
|
|
|
216 |
209 |
dgisselq |
// On our next clock, our result with either be the registered i_pc
|
217 |
|
|
// value from the last clock (if isrc), otherwise r_lastpc
|
218 |
|
|
assign o_pc = (isrc) ? r_pc : r_lastpc;
|
219 |
|
|
// The same applies for determining what the next output instruction
|
220 |
|
|
// will be. We just read it in the last clock, now we just need to
|
221 |
|
|
// select between the two possibilities we just read.
|
222 |
|
|
assign o_insn= (isrc) ? r_pc_cache : r_last_cache;
|
223 |
|
|
|
224 |
|
|
|
225 |
|
|
/////////////////////////////////////////////////
|
226 |
|
|
//
|
227 |
|
|
// Read the tag value associated with this tcache line
|
228 |
|
|
//
|
229 |
|
|
/////////////////////////////////////////////////
|
230 |
|
|
//
|
231 |
|
|
//
|
232 |
|
|
|
233 |
|
|
//
|
234 |
|
|
// Read the tag value associated with this i_pc value
|
235 |
176 |
dgisselq |
initial tagvalipc = 0;
|
236 |
|
|
always @(posedge i_clk)
|
237 |
209 |
dgisselq |
tagvalipc <= cache_tags[i_pc[(CW+1):LS+2]];
|
238 |
|
|
|
239 |
|
|
|
240 |
|
|
//
|
241 |
|
|
// Read the tag value associated with the lastpc value, from what
|
242 |
|
|
// i_pc was when we could not tell if this value was in our cache or
|
243 |
|
|
// not, or perhaps from when we determined that i was not in the cache.
|
244 |
176 |
dgisselq |
initial tagvallst = 0;
|
245 |
|
|
always @(posedge i_clk)
|
246 |
209 |
dgisselq |
tagvallst <= cache_tags[lastpc[(CW+1):LS+2]];
|
247 |
69 |
dgisselq |
|
248 |
209 |
dgisselq |
// Select from between these two values on the next clock
|
249 |
|
|
assign tagval = (isrc)?tagvalipc : tagvallst;
|
250 |
|
|
|
251 |
69 |
dgisselq |
// i_pc will only increment when everything else isn't stalled, thus
|
252 |
|
|
// we can set it without worrying about that. Doing this enables
|
253 |
|
|
// us to work in spite of stalls. For example, if the next address
|
254 |
|
|
// isn't valid, but the decoder is stalled, get the next address
|
255 |
|
|
// anyway.
|
256 |
|
|
initial lastpc = 0;
|
257 |
|
|
always @(posedge i_clk)
|
258 |
209 |
dgisselq |
if (w_advance)
|
259 |
|
|
lastpc <= i_pc;
|
260 |
69 |
dgisselq |
|
261 |
209 |
dgisselq |
assign lasttag = lastpc[(AW+1):LS+2];
|
262 |
69 |
dgisselq |
|
263 |
209 |
dgisselq |
/////////////////////////////////////////////////
|
264 |
|
|
//
|
265 |
|
|
// Use the tag value to determine if our output instruction will be
|
266 |
|
|
// valid.
|
267 |
|
|
//
|
268 |
|
|
/////////////////////////////////////////////////
|
269 |
|
|
//
|
270 |
|
|
//
|
271 |
|
|
assign w_v_from_pc = ((i_pc[(AW+1):LS+2] == lasttag)
|
272 |
|
|
&&(tagval == i_pc[(AW+1):CW+2])
|
273 |
|
|
&&(valid_mask[i_pc[(CW+1):LS+2]]));
|
274 |
|
|
assign w_v_from_last = ((tagval == lastpc[(AW+1):CW+2])
|
275 |
|
|
&&(valid_mask[lastpc[(CW+1):LS+2]]));
|
276 |
69 |
dgisselq |
|
277 |
|
|
initial delay = 2'h3;
|
278 |
|
|
always @(posedge i_clk)
|
279 |
209 |
dgisselq |
if ((i_reset)||(i_clear_cache)||(w_advance))
|
280 |
|
|
begin
|
281 |
|
|
// Source our valid signal from i_pc
|
282 |
|
|
rvsrc <= 1'b1;
|
283 |
|
|
// Delay at least two clocks before declaring that
|
284 |
|
|
// we have an invalid result. This will give us time
|
285 |
|
|
// to check the tag value of what's in the cache.
|
286 |
|
|
delay <= 2'h2;
|
287 |
|
|
end else if ((!r_v)&&(!o_illegal)) begin
|
288 |
|
|
// If we aren't sourcing our valid signal from the
|
289 |
|
|
// i_pc clock, then we are sourcing it from the
|
290 |
|
|
// lastpc clock (one clock later). If r_v still
|
291 |
|
|
// isn't valid, we may need to make a bus request.
|
292 |
|
|
// Apply our timer and timeout.
|
293 |
|
|
rvsrc <= 1'b0;
|
294 |
|
|
|
295 |
|
|
// Delay is two once the bus starts, in case the
|
296 |
|
|
// bus transaction needs to be restarted upon completion
|
297 |
|
|
// This might happen if, after we start loading the
|
298 |
|
|
// cache, we discover a branch. The cache load will
|
299 |
|
|
// still complete, but the branches address needs to be
|
300 |
|
|
// the onen we jump to. This may mean we need to load
|
301 |
|
|
// the cache twice.
|
302 |
|
|
if (o_wb_cyc)
|
303 |
69 |
dgisselq |
delay <= 2'h2;
|
304 |
209 |
dgisselq |
else if (delay != 0)
|
305 |
|
|
delay <= delay + 2'b11; // i.e. delay -= 1;
|
306 |
|
|
end else begin
|
307 |
|
|
// After sourcing our output from i_pc, if it wasn't
|
308 |
|
|
// accepted, source the instruction from the lastpc valid
|
309 |
|
|
// determination instead
|
310 |
|
|
rvsrc <= 1'b0;
|
311 |
|
|
if (o_illegal)
|
312 |
|
|
delay <= 2'h2;
|
313 |
|
|
end
|
314 |
|
|
|
315 |
|
|
wire w_invalidate_result;
|
316 |
|
|
assign w_invalidate_result = (i_reset)||(i_clear_cache);
|
317 |
|
|
|
318 |
|
|
reg r_prior_illegal;
|
319 |
|
|
initial r_prior_illegal = 0;
|
320 |
|
|
initial r_new_request = 0;
|
321 |
|
|
initial r_v_from_pc = 0;
|
322 |
|
|
initial r_v_from_last = 0;
|
323 |
176 |
dgisselq |
always @(posedge i_clk)
|
324 |
209 |
dgisselq |
begin
|
325 |
|
|
r_new_request <= w_invalidate_result;
|
326 |
|
|
r_v_from_pc <= (w_v_from_pc)&&(!w_invalidate_result)
|
327 |
|
|
&&(!o_illegal);
|
328 |
|
|
r_v_from_last <= (w_v_from_last)&&(!w_invalidate_result);
|
329 |
|
|
|
330 |
|
|
r_prior_illegal <= (o_wb_cyc)&&(i_wb_err);
|
331 |
|
|
end
|
332 |
|
|
|
333 |
|
|
// Now use rvsrc to determine which of the two valid flags we'll be
|
334 |
|
|
// using: r_v_from_pc (the i_pc address), or r_v_from_last (the lastpc
|
335 |
|
|
// address)
|
336 |
|
|
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last))&&(!r_new_request);
|
337 |
|
|
assign o_valid = (((rvsrc)?(r_v_from_pc):(r_v_from_last))
|
338 |
|
|
||(o_illegal))
|
339 |
|
|
&&(!i_new_pc)&&(!r_prior_illegal);
|
340 |
|
|
|
341 |
|
|
/////////////////////////////////////////////////
|
342 |
|
|
//
|
343 |
|
|
// If the instruction isn't in our cache, then we need to load
|
344 |
|
|
// a new cache line from memory.
|
345 |
|
|
//
|
346 |
|
|
/////////////////////////////////////////////////
|
347 |
|
|
//
|
348 |
|
|
//
|
349 |
|
|
initial needload = 1'b0;
|
350 |
176 |
dgisselq |
always @(posedge i_clk)
|
351 |
209 |
dgisselq |
if ((i_clear_cache)||(o_wb_cyc))
|
352 |
|
|
needload <= 1'b0;
|
353 |
|
|
else if ((w_advance)&&(!o_illegal))
|
354 |
|
|
needload <= 1'b0;
|
355 |
|
|
else
|
356 |
|
|
needload <= (delay==0)&&(!w_v_from_last)
|
357 |
|
|
// Prevent us from reloading an illegal address
|
358 |
|
|
// (i.e. one that produced a bus error) over and over
|
359 |
|
|
// and over again
|
360 |
|
|
&&((!illegal_valid)
|
361 |
|
|
||(lastpc[(AW+1):LS+2] != illegal_cache));
|
362 |
69 |
dgisselq |
|
363 |
209 |
dgisselq |
//
|
364 |
|
|
// Working from the rule that you want to keep complex logic out of
|
365 |
|
|
// a state machine if possible, we calculate a "last_stb" value one
|
366 |
|
|
// clock ahead of time. Hence, any time a request is accepted, if
|
367 |
|
|
// last_stb is also true we'll know we need to drop the strobe line,
|
368 |
|
|
// having finished requesting a complete cache line.
|
369 |
|
|
initial last_addr = 1'b0;
|
370 |
|
|
always @(posedge i_clk)
|
371 |
|
|
if (!o_wb_cyc)
|
372 |
|
|
last_addr <= 1'b0;
|
373 |
|
|
else if ((o_wb_addr[(LS-1):1] == {(LS-1){1'b1}})
|
374 |
|
|
&&((!i_wb_stall)|(o_wb_addr[0])))
|
375 |
|
|
last_addr <= 1'b1;
|
376 |
69 |
dgisselq |
|
377 |
209 |
dgisselq |
//
|
378 |
|
|
// "last_ack" is almost identical to last_addr, save that this
|
379 |
|
|
// will be true on the same clock as the last acknowledgment from the
|
380 |
|
|
// bus. The state machine logic will use this to determine when to
|
381 |
|
|
// get off the bus and end the wishbone bus cycle.
|
382 |
176 |
dgisselq |
initial last_ack = 1'b0;
|
383 |
|
|
always @(posedge i_clk)
|
384 |
|
|
last_ack <= (o_wb_cyc)&&(
|
385 |
209 |
dgisselq |
(wraddr[(LS-1):1]=={(LS-1){1'b1}})
|
386 |
|
|
&&((wraddr[0])||(i_wb_ack)));
|
387 |
69 |
dgisselq |
|
388 |
209 |
dgisselq |
initial bus_abort = 1'b0;
|
389 |
176 |
dgisselq |
always @(posedge i_clk)
|
390 |
209 |
dgisselq |
if (!o_wb_cyc)
|
391 |
|
|
bus_abort <= 1'b0;
|
392 |
|
|
else if ((i_clear_cache)||(i_new_pc))
|
393 |
|
|
bus_abort <= 1'b1;
|
394 |
176 |
dgisselq |
|
395 |
209 |
dgisselq |
//
|
396 |
|
|
// Here's the difficult piece of state machine logic--the part that
|
397 |
|
|
// determines o_wb_cyc and o_wb_stb. We've already moved most of the
|
398 |
|
|
// complicated logic off of this statemachine, calculating it one cycle
|
399 |
|
|
// early. As a result, this is a fairly easy piece of logic.
|
400 |
69 |
dgisselq |
initial o_wb_cyc = 1'b0;
|
401 |
|
|
initial o_wb_stb = 1'b0;
|
402 |
|
|
always @(posedge i_clk)
|
403 |
209 |
dgisselq |
if ((i_reset)||(i_clear_cache))
|
404 |
|
|
begin
|
405 |
|
|
o_wb_cyc <= 1'b0;
|
406 |
|
|
o_wb_stb <= 1'b0;
|
407 |
|
|
end else if (o_wb_cyc)
|
408 |
|
|
begin
|
409 |
|
|
if (i_wb_err)
|
410 |
69 |
dgisselq |
o_wb_stb <= 1'b0;
|
411 |
209 |
dgisselq |
else if ((o_wb_stb)&&(!i_wb_stall)&&(last_addr))
|
412 |
|
|
o_wb_stb <= 1'b0;
|
413 |
69 |
dgisselq |
|
414 |
209 |
dgisselq |
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
|
415 |
|
|
o_wb_cyc <= 1'b0;
|
416 |
82 |
dgisselq |
|
417 |
209 |
dgisselq |
end else if ((needload)&&(!i_new_pc))
|
418 |
|
|
begin
|
419 |
|
|
o_wb_cyc <= 1'b1;
|
420 |
|
|
o_wb_stb <= 1'b1;
|
421 |
|
|
end
|
422 |
69 |
dgisselq |
|
423 |
209 |
dgisselq |
// If we are reading from this cache line, then once we get the first
|
424 |
|
|
// acknowledgement, this cache line has the new tag value
|
425 |
|
|
always @(posedge i_clk)
|
426 |
|
|
if ((o_wb_cyc)&&(i_wb_ack))
|
427 |
|
|
cache_tags[o_wb_addr[(CW-1):LS]] <= o_wb_addr[(AW-1):CW];
|
428 |
69 |
dgisselq |
|
429 |
209 |
dgisselq |
|
430 |
|
|
// On each acknowledgment, increment the address we use to write into
|
431 |
|
|
// our cache. Hence, this is the write address into our cache block
|
432 |
|
|
// RAM.
|
433 |
|
|
initial wraddr = 0;
|
434 |
176 |
dgisselq |
always @(posedge i_clk)
|
435 |
209 |
dgisselq |
if ((o_wb_cyc)&&(i_wb_ack)&&(!last_ack))
|
436 |
|
|
wraddr <= wraddr + 1'b1;
|
437 |
|
|
else if (!o_wb_cyc)
|
438 |
|
|
wraddr <= { lastpc[(CW+1):LS+2], {(LS){1'b0}} };
|
439 |
|
|
|
440 |
|
|
//
|
441 |
|
|
// The wishbone request address. This has meaning anytime o_wb_stb
|
442 |
|
|
// is active, and needs to be incremented any time an address is
|
443 |
|
|
// accepted--WITH THE EXCEPTION OF THE LAST ADDRESS. We need to keep
|
444 |
|
|
// this steady for that last address, unless the last address returns
|
445 |
|
|
// a bus error. In that case, the whole cache line will be marked as
|
446 |
|
|
// invalid--but we'll need the value of this register to know how
|
447 |
|
|
// to do that propertly.
|
448 |
|
|
initial o_wb_addr = {(AW){1'b0}};
|
449 |
176 |
dgisselq |
always @(posedge i_clk)
|
450 |
209 |
dgisselq |
if ((o_wb_stb)&&(!i_wb_stall)&&(!last_addr))
|
451 |
|
|
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1;
|
452 |
|
|
else if (!o_wb_cyc)
|
453 |
|
|
o_wb_addr <= { lastpc[(AW+1):LS+2], {(LS){1'b0}} };
|
454 |
176 |
dgisselq |
|
455 |
209 |
dgisselq |
// Since it is impossible to initialize an array, our cache will start
|
456 |
|
|
// up cache uninitialized. We'll also never get a valid ack without
|
457 |
|
|
// cyc being active, although we might get one on the clock after
|
458 |
|
|
// cyc was active--so we need to test and gate on whether o_wb_cyc
|
459 |
|
|
// is true.
|
460 |
|
|
//
|
461 |
|
|
// wraddr will advance forward on every clock cycle where ack is true,
|
462 |
|
|
// hence we don't need to check i_wb_ack here. This will work because
|
463 |
|
|
// multiple writes to the same address, ending with a valid write,
|
464 |
|
|
// will always yield the valid write's value only after our bus cycle
|
465 |
|
|
// is over.
|
466 |
69 |
dgisselq |
always @(posedge i_clk)
|
467 |
209 |
dgisselq |
if (o_wb_cyc)
|
468 |
|
|
cache[wraddr] <= i_wb_data;
|
469 |
69 |
dgisselq |
|
470 |
|
|
// VMask ... is a section loaded?
|
471 |
209 |
dgisselq |
// Note "svmask". It's purpose is to delay the valid_mask setting by
|
472 |
|
|
// one clock, so that we can insure the right value of the cache is
|
473 |
|
|
// loaded before declaring that the cache line is valid. Without
|
474 |
|
|
// this, the cache line would get read, and the instruction would
|
475 |
|
|
// read from the last cache line.
|
476 |
|
|
initial valid_mask = 0;
|
477 |
176 |
dgisselq |
initial svmask = 1'b0;
|
478 |
69 |
dgisselq |
always @(posedge i_clk)
|
479 |
209 |
dgisselq |
if ((i_reset)||(i_clear_cache))
|
480 |
|
|
begin
|
481 |
|
|
valid_mask <= 0;
|
482 |
|
|
svmask<= 1'b0;
|
483 |
|
|
end else begin
|
484 |
|
|
svmask <= ((o_wb_cyc)&&(i_wb_ack)&&(last_ack)&&(!bus_abort));
|
485 |
|
|
|
486 |
|
|
if (svmask)
|
487 |
|
|
valid_mask[saddr] <= (!bus_abort);
|
488 |
|
|
if ((!o_wb_cyc)&&(needload))
|
489 |
|
|
valid_mask[lastpc[(CW+1):LS+2]] <= 1'b0;
|
490 |
|
|
`ifdef NOT_YET_READY
|
491 |
|
|
//
|
492 |
|
|
// MMU code
|
493 |
|
|
//
|
494 |
|
|
if (mmu_inval)
|
495 |
|
|
valid_mask[mmu_mskadr] <= 1'b0;
|
496 |
|
|
`endif
|
497 |
|
|
end
|
498 |
|
|
|
499 |
|
|
always @(posedge i_clk)
|
500 |
|
|
if ((o_wb_cyc)&&(i_wb_ack))
|
501 |
|
|
saddr <= wraddr[(CW-1):LS];
|
502 |
|
|
// MMU code
|
503 |
|
|
//
|
504 |
|
|
//
|
505 |
|
|
`ifdef NOT_YET_READY
|
506 |
|
|
parameter [0:0] USE_MMU = 1'b1;
|
507 |
|
|
generate if (USE_MMU)
|
508 |
|
|
begin
|
509 |
|
|
reg [(PAW-CW-1):0] ptag [0:((1<<(LGLINES))-1)];
|
510 |
|
|
reg mmu_pre_inval, r_mmu_inval;
|
511 |
|
|
reg [(PAW-CW-1):0] mmu_pre_tag, mmu_pre_padr;
|
512 |
|
|
reg [(CW-LS-1):0] r_mmu_mskadr;
|
513 |
|
|
|
514 |
|
|
initial mmu_pre_inval = 0;
|
515 |
|
|
initial mmu_pre_tag = 0;
|
516 |
|
|
initial mmu_pre_padr = 0;
|
517 |
|
|
initial mmu_pre2_inval = 0;
|
518 |
|
|
initial mmu_pre2_mskadr = 0;
|
519 |
|
|
|
520 |
|
|
always @(posedge i_clk)
|
521 |
|
|
if ((o_wb_cyc)&&(!last_addr)&&(i_mmu_ack))
|
522 |
|
|
ptag[i_mmu_paddr[(CW-1):LS]] <= i_mmu_paddr[(PAW-1):CW];
|
523 |
|
|
|
524 |
|
|
always @(posedge i_clk)
|
525 |
|
|
if (i_reset)
|
526 |
176 |
dgisselq |
begin
|
527 |
209 |
dgisselq |
mmu_pre_inval <= 0;
|
528 |
|
|
r_mmu_inval <= 0;
|
529 |
|
|
end else begin
|
530 |
|
|
mmu_pre_inval <= (i_mmu_ack)&&(i_mmu_we);
|
531 |
|
|
r_mmu_inval <= (mmu_pre_inval)&&(mmu_pre_inval)
|
532 |
|
|
&&(mmu_pre_tag == mmu_pre_paddr);
|
533 |
176 |
dgisselq |
end
|
534 |
209 |
dgisselq |
|
535 |
|
|
always @(posedge i_clk)
|
536 |
|
|
mmu_pre_tag <= ptag[i_mmu_paddr[(CW-1):LS]];
|
537 |
|
|
|
538 |
|
|
always @(posedge i_clk)
|
539 |
|
|
begin
|
540 |
|
|
mmu_pre_padr <= i_mmu_paddr[(PAW-1):CW];
|
541 |
|
|
|
542 |
|
|
r_mmu_mskadr <= mmu_pre_padr[(PAW-LS-1):(CW-LS)];
|
543 |
118 |
dgisselq |
end
|
544 |
69 |
dgisselq |
|
545 |
209 |
dgisselq |
assign mmu_inval = r_mmu_inval;
|
546 |
|
|
assign mmu_mskadr = r_mmu_mskadr;
|
547 |
|
|
end else begin
|
548 |
|
|
assign mmu_inval = 0;
|
549 |
|
|
assign mmu_mskadr = 0;
|
550 |
|
|
end endgenerate
|
551 |
|
|
`endif
|
552 |
|
|
|
553 |
|
|
/////////////////////////////////////////////////
|
554 |
|
|
//
|
555 |
|
|
// Handle bus errors here. If a bus read request
|
556 |
|
|
// returns an error, then we'll mark the entire
|
557 |
|
|
// line as having a (valid) illegal value.
|
558 |
|
|
//
|
559 |
|
|
/////////////////////////////////////////////////
|
560 |
|
|
//
|
561 |
|
|
//
|
562 |
|
|
//
|
563 |
|
|
//
|
564 |
69 |
dgisselq |
initial illegal_cache = 0;
|
565 |
71 |
dgisselq |
initial illegal_valid = 0;
|
566 |
69 |
dgisselq |
always @(posedge i_clk)
|
567 |
209 |
dgisselq |
if ((i_reset)||(i_clear_cache))
|
568 |
|
|
begin
|
569 |
|
|
illegal_cache <= 0;
|
570 |
|
|
illegal_valid <= 0;
|
571 |
|
|
end else if ((o_wb_cyc)&&(i_wb_err))
|
572 |
|
|
begin
|
573 |
|
|
illegal_cache <= o_wb_addr[(AW-1):LS];
|
574 |
|
|
illegal_valid <= 1'b1;
|
575 |
|
|
end
|
576 |
|
|
|
577 |
|
|
initial o_illegal = 1'b0;
|
578 |
|
|
always @(posedge i_clk)
|
579 |
|
|
if ((i_reset)||(i_clear_cache)||(i_new_pc))
|
580 |
|
|
o_illegal <= 1'b0;
|
581 |
|
|
else if ((o_illegal)||((o_valid)&&(i_stall_n)))
|
582 |
|
|
o_illegal <= 1'b0;
|
583 |
|
|
else
|
584 |
|
|
o_illegal <= (illegal_valid)
|
585 |
|
|
&&(illegal_cache == lastpc[(AW+1):LS+2]);
|
586 |
|
|
|
587 |
|
|
`ifdef FORMAL
|
588 |
|
|
//
|
589 |
|
|
//
|
590 |
|
|
// Generic setup
|
591 |
|
|
//
|
592 |
|
|
//
|
593 |
|
|
`ifdef PFCACHE
|
594 |
|
|
`define ASSUME assume
|
595 |
|
|
`else
|
596 |
|
|
`define ASSUME assert
|
597 |
|
|
`define STEP_CLOCK
|
598 |
|
|
`endif
|
599 |
|
|
|
600 |
|
|
// Keep track of a flag telling us whether or not $past()
|
601 |
|
|
// will return valid results
|
602 |
|
|
reg f_past_valid;
|
603 |
|
|
initial f_past_valid = 1'b0;
|
604 |
|
|
always @(posedge i_clk)
|
605 |
|
|
f_past_valid = 1'b1;
|
606 |
|
|
always @(*)
|
607 |
|
|
if (!f_past_valid)
|
608 |
|
|
`ASSUME(i_reset);
|
609 |
|
|
|
610 |
|
|
/////////////////////////////////////////////////
|
611 |
|
|
//
|
612 |
|
|
//
|
613 |
|
|
// Assumptions about our inputs
|
614 |
|
|
//
|
615 |
|
|
//
|
616 |
|
|
/////////////////////////////////////////////////
|
617 |
|
|
|
618 |
|
|
|
619 |
|
|
`ifdef PFCACHE
|
620 |
|
|
//
|
621 |
|
|
// Assume that resets, new-pc commands, and clear-cache commands
|
622 |
|
|
// are never more than pulses--one clock wide at most.
|
623 |
|
|
//
|
624 |
|
|
// It may be that the CPU treats us differently. We'll only assume
|
625 |
|
|
// our solver to this here.
|
626 |
|
|
always @(posedge i_clk)
|
627 |
|
|
if (!f_past_valid)
|
628 |
|
|
begin
|
629 |
|
|
if ($past(i_reset))
|
630 |
|
|
assume(!i_reset);
|
631 |
|
|
if ($past(i_new_pc))
|
632 |
|
|
assume(!i_new_pc);
|
633 |
|
|
if ($past(i_clear_cache))
|
634 |
|
|
assume(!i_clear_cache);
|
635 |
|
|
end
|
636 |
|
|
`endif
|
637 |
|
|
|
638 |
|
|
//
|
639 |
|
|
// Assume we start from a reset condition
|
640 |
|
|
initial `ASSUME(i_reset);
|
641 |
|
|
|
642 |
|
|
// Assume that any reset is either accompanied by a new address,
|
643 |
|
|
// or a new address immediately follows it.
|
644 |
|
|
always @(posedge i_clk)
|
645 |
|
|
if ((f_past_valid)&&($past(i_reset)))
|
646 |
|
|
`ASSUME(i_new_pc);
|
647 |
|
|
//
|
648 |
|
|
// Let's make some assumptions about how long it takes our
|
649 |
|
|
// phantom bus and phantom CPU to respond.
|
650 |
|
|
//
|
651 |
|
|
// These delays need to be long enough to flush out any potential
|
652 |
|
|
// errors, yet still short enough that the formal method doesn't
|
653 |
|
|
// take forever to solve.
|
654 |
|
|
//
|
655 |
|
|
localparam F_CPU_DELAY = 4;
|
656 |
|
|
reg [4:0] f_cpu_delay;
|
657 |
|
|
|
658 |
|
|
// Now, let's repeat this bit but now looking at the delay the CPU
|
659 |
|
|
// takes to accept an instruction.
|
660 |
|
|
always @(posedge i_clk)
|
661 |
|
|
// If no instruction is ready, then keep our counter at zero
|
662 |
|
|
if ((!o_valid)||(i_stall_n))
|
663 |
|
|
f_cpu_delay <= 0;
|
664 |
|
|
else
|
665 |
|
|
// Otherwise, count the clocks the CPU takes to respond
|
666 |
|
|
f_cpu_delay <= f_cpu_delay + 1'b1;
|
667 |
|
|
|
668 |
|
|
`ifdef PFCACHE
|
669 |
|
|
always @(posedge i_clk)
|
670 |
|
|
assume(f_cpu_delay < F_CPU_DELAY);
|
671 |
|
|
`endif
|
672 |
|
|
|
673 |
|
|
always @(posedge i_clk)
|
674 |
|
|
if ($past(i_reset || i_clear_cache))
|
675 |
|
|
assume(i_stall_n);
|
676 |
|
|
else if ($past(i_stall_n && !o_valid))
|
677 |
|
|
assume(i_stall_n);
|
678 |
|
|
else if (i_new_pc)
|
679 |
|
|
assume(i_stall_n);
|
680 |
|
|
|
681 |
|
|
/////////////////////////////////////////////////
|
682 |
|
|
//
|
683 |
|
|
//
|
684 |
|
|
// Assertions about our outputs
|
685 |
|
|
//
|
686 |
|
|
//
|
687 |
|
|
/////////////////////////////////////////////////
|
688 |
|
|
|
689 |
|
|
localparam F_LGDEPTH=LS+1;
|
690 |
|
|
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
|
691 |
|
|
|
692 |
|
|
fwb_master #(.AW(AW), .DW(BUSW), .F_LGDEPTH(F_LGDEPTH),
|
693 |
|
|
.F_MAX_STALL(2), .F_MAX_ACK_DELAY(3),
|
694 |
|
|
.F_MAX_REQUESTS(1<<LS), .F_OPT_SOURCE(1),
|
695 |
|
|
.F_OPT_RMW_BUS_OPTION(0),
|
696 |
|
|
.F_OPT_DISCONTINUOUS(0))
|
697 |
|
|
f_wbm(i_clk, i_reset,
|
698 |
|
|
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0,
|
699 |
|
|
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
|
700 |
|
|
f_nreqs, f_nacks, f_outstanding);
|
701 |
|
|
|
702 |
|
|
// writes are also illegal for a prefetch.
|
703 |
|
|
always @(posedge i_clk)
|
704 |
|
|
if (o_wb_stb)
|
705 |
|
|
assert(!o_wb_we);
|
706 |
|
|
|
707 |
|
|
always @(posedge i_clk)
|
708 |
|
|
begin
|
709 |
|
|
assert(f_nreqs <= (1<<LS));
|
710 |
|
|
if ((o_wb_cyc)&&(o_wb_stb))
|
711 |
|
|
assert(f_nreqs == o_wb_addr[(LS-1):0]);
|
712 |
|
|
if ((f_past_valid)&&($past(o_wb_cyc))
|
713 |
|
|
&&(!o_wb_stb)&&(!$past(i_wb_err || i_reset || i_clear_cache)))
|
714 |
|
|
assert(f_nreqs == (1<<LS));
|
715 |
|
|
end
|
716 |
|
|
|
717 |
|
|
always @(posedge i_clk)
|
718 |
|
|
if (f_past_valid)
|
719 |
|
|
begin
|
720 |
|
|
if ((!o_wb_cyc)&&($past(o_wb_cyc))&&(!$past(i_reset))
|
721 |
|
|
&&(!$past(i_clear_cache)) &&(!$past(i_wb_err)))
|
722 |
|
|
assert(f_nacks == (1<<LS));
|
723 |
|
|
else if (o_wb_cyc)
|
724 |
|
|
assert(f_nacks[(LS-1):0] == wraddr[(LS-1):0]);
|
725 |
|
|
end
|
726 |
|
|
|
727 |
|
|
// The last-ack line
|
728 |
|
|
always @(posedge i_clk)
|
729 |
|
|
if (o_wb_cyc)
|
730 |
|
|
assert(last_ack == (f_nacks == ((1<<LS)-1)));
|
731 |
|
|
|
732 |
|
|
// The valid line for whats being read
|
733 |
|
|
always @(posedge i_clk)
|
734 |
|
|
if (o_wb_cyc)
|
735 |
|
|
assert(!valid_mask[o_wb_addr[CW-1:LS]]);
|
736 |
|
|
|
737 |
|
|
always @(posedge i_clk)
|
738 |
|
|
if ((illegal_valid)&&(o_wb_cyc))
|
739 |
|
|
assert(o_wb_addr[AW-1:LS] != illegal_cache);
|
740 |
|
|
|
741 |
|
|
reg [((1<<(LGLINES))-1):0] f_past_valid_mask;
|
742 |
|
|
initial f_past_valid_mask = 0;
|
743 |
|
|
always @(posedge i_clk)
|
744 |
|
|
f_past_valid_mask = valid_mask;
|
745 |
|
|
|
746 |
|
|
always @(posedge i_clk)
|
747 |
|
|
if ((o_valid)&&($past(!o_valid || !o_illegal)))
|
748 |
|
|
assert((!o_wb_cyc)
|
749 |
|
|
||(o_wb_addr[AW-1:LS] != o_pc[AW+1:LS+2]));
|
750 |
|
|
always @(posedge i_clk)
|
751 |
|
|
if (illegal_valid)
|
752 |
|
|
begin
|
753 |
|
|
assert((!o_wb_cyc)
|
754 |
|
|
||(o_wb_addr[AW-1:LS] != illegal_cache));
|
755 |
|
|
|
756 |
|
|
// The illegal cache line should never be valid within our
|
757 |
|
|
// cache
|
758 |
|
|
assert((!valid_mask[illegal_cache[CW-1:LS]])
|
759 |
|
|
||(cache_tags[illegal_cache[CW-1:LS]]
|
760 |
|
|
!= illegal_cache[AW-1:CW]));
|
761 |
|
|
end
|
762 |
|
|
|
763 |
|
|
/////////////////////////////////////////////////////
|
764 |
|
|
//
|
765 |
|
|
//
|
766 |
|
|
// Assertions about our return responses to the CPU
|
767 |
|
|
//
|
768 |
|
|
//
|
769 |
|
|
/////////////////////////////////////////////////////
|
770 |
|
|
|
771 |
|
|
|
772 |
|
|
always @(posedge i_clk)
|
773 |
|
|
if ((f_past_valid)&&($past(o_wb_cyc)))
|
774 |
|
|
assert(o_wb_addr[(AW-1):LS] == $past(o_wb_addr[(AW-1):LS]));
|
775 |
|
|
|
776 |
|
|
// Consider it invalid to present the CPU with the same instruction
|
777 |
|
|
// twice in a row.
|
778 |
|
|
always @(posedge i_clk)
|
779 |
|
|
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n))&&(o_valid))
|
780 |
|
|
assert(o_pc != $past(o_pc));
|
781 |
|
|
|
782 |
|
|
always @(posedge i_clk)
|
783 |
|
|
if (o_valid)
|
784 |
|
|
begin
|
785 |
|
|
if (!o_illegal)
|
786 |
71 |
dgisselq |
begin
|
787 |
209 |
dgisselq |
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]);
|
788 |
|
|
assert(valid_mask[o_pc[(CW+1):LS+2]] || (o_illegal));
|
789 |
|
|
assert(o_insn == cache[o_pc[(CW+1):2]]);
|
790 |
|
|
assert((!illegal_valid)
|
791 |
|
|
||((illegal_cache != o_pc[(AW+1):LS+2])));
|
792 |
71 |
dgisselq |
end
|
793 |
69 |
dgisselq |
|
794 |
209 |
dgisselq |
assert(o_illegal == ($past(illegal_valid)
|
795 |
|
|
&&($past(illegal_cache)== o_pc[(AW+1):LS+2])));
|
796 |
|
|
end
|
797 |
|
|
|
798 |
|
|
always @(*)
|
799 |
|
|
begin
|
800 |
|
|
`ASSUME(i_pc[1:0] == 2'b00);
|
801 |
|
|
assert(o_pc[1:0] == 2'b00);
|
802 |
|
|
assert(r_pc[1:0] == 2'b00);
|
803 |
|
|
assert(r_lastpc[1:0] == 2'b00);
|
804 |
|
|
end
|
805 |
|
|
|
806 |
|
|
reg [(AW+1):0] f_next_pc;
|
807 |
|
|
|
808 |
69 |
dgisselq |
always @(posedge i_clk)
|
809 |
209 |
dgisselq |
if ((f_past_valid)&&(!$past(i_reset)))
|
810 |
|
|
begin
|
811 |
|
|
if (isrc)
|
812 |
|
|
assert(lastpc == r_pc);
|
813 |
71 |
dgisselq |
else
|
814 |
209 |
dgisselq |
assert(lastpc + 4== r_pc);
|
815 |
|
|
end
|
816 |
69 |
dgisselq |
|
817 |
209 |
dgisselq |
always @(posedge i_clk)
|
818 |
|
|
if (i_new_pc)
|
819 |
|
|
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
|
820 |
|
|
else if ((i_stall_n)&&(r_v))
|
821 |
|
|
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
|
822 |
|
|
always @(*)
|
823 |
|
|
if (!i_new_pc)
|
824 |
|
|
`ASSUME(i_pc == f_next_pc);
|
825 |
|
|
|
826 |
|
|
always @(posedge i_clk)
|
827 |
|
|
if ((f_past_valid)&&(o_valid)&&($past(o_valid))
|
828 |
|
|
&&(!$past(i_reset))
|
829 |
|
|
&&(!$past(i_new_pc))
|
830 |
|
|
&&(!$past(i_stall_n))
|
831 |
|
|
&&(!o_illegal))
|
832 |
|
|
begin
|
833 |
|
|
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]);
|
834 |
|
|
end
|
835 |
|
|
|
836 |
|
|
//
|
837 |
|
|
// If an instruction is accepted, we should *always* move on to another
|
838 |
|
|
// instruction. The only exception is following an i_new_pc (or
|
839 |
|
|
// other invalidator), at which point the next instruction should
|
840 |
|
|
// be invalid.
|
841 |
|
|
always @(posedge i_clk)
|
842 |
|
|
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n)))
|
843 |
|
|
begin
|
844 |
|
|
// Should always advance the instruction
|
845 |
|
|
assert((!o_valid)||(o_pc != $past(o_pc)));
|
846 |
|
|
end
|
847 |
|
|
|
848 |
|
|
//
|
849 |
|
|
// Once an instruction becomes valid, it should never become invalid
|
850 |
|
|
// unless there's been a request for a new instruction.
|
851 |
|
|
always @(posedge i_clk)
|
852 |
|
|
if ((f_past_valid)&&($past(!i_reset && !i_clear_cache && !i_new_pc))
|
853 |
|
|
&&($past(o_valid && !i_stall_n))
|
854 |
|
|
&&(!i_new_pc))
|
855 |
|
|
begin
|
856 |
|
|
if ((!$past(o_illegal))&&(!$past(o_wb_cyc && i_wb_err)))
|
857 |
|
|
begin
|
858 |
|
|
assert(o_valid);
|
859 |
|
|
assert($stable(o_illegal));
|
860 |
|
|
assert($stable(o_insn));
|
861 |
|
|
end else
|
862 |
|
|
assert((o_illegal)||(!o_valid));
|
863 |
|
|
end
|
864 |
|
|
`ifdef PFCACHE
|
865 |
|
|
/////////////////////////////////////////////////////
|
866 |
|
|
//
|
867 |
|
|
//
|
868 |
|
|
// Assertions associated with a response to a known
|
869 |
|
|
// address request
|
870 |
|
|
//
|
871 |
|
|
//
|
872 |
|
|
/////////////////////////////////////////////////////
|
873 |
|
|
|
874 |
|
|
|
875 |
|
|
(* anyconst *) reg [AW:0] f_const_addr;
|
876 |
|
|
(* anyconst *) reg [BUSW-1:0] f_const_insn;
|
877 |
|
|
|
878 |
|
|
wire f_this_pc, f_this_insn, f_this_data, f_this_line,
|
879 |
|
|
f_this_ack, f_this_tag; // f_this_addr;
|
880 |
|
|
assign f_this_pc = (o_pc == { f_const_addr[AW-1:0], 2'b00 });
|
881 |
|
|
// assign f_this_addr = (o_wb_addr == f_const_addr[AW-1:0] );
|
882 |
|
|
assign f_this_insn = (o_insn == f_const_insn);
|
883 |
|
|
assign f_this_data = (i_wb_data == f_const_insn);
|
884 |
|
|
assign f_this_line = (o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS]);
|
885 |
|
|
assign f_this_ack = (f_this_line)&&(f_nacks == f_const_addr[LS-1:0]);
|
886 |
|
|
assign f_this_tag = (tagval == f_const_addr[AW-1:CW]);
|
887 |
|
|
|
888 |
|
|
always @(posedge i_clk)
|
889 |
|
|
if ((o_valid)&&(f_this_pc)&&(!$past(o_illegal)))
|
890 |
|
|
begin
|
891 |
|
|
assert(o_illegal == f_const_addr[AW]);
|
892 |
|
|
if (!o_illegal)
|
893 |
|
|
begin
|
894 |
|
|
assert(f_this_insn);
|
895 |
|
|
assert(f_this_tag);
|
896 |
|
|
end
|
897 |
|
|
end
|
898 |
|
|
|
899 |
|
|
always @(*)
|
900 |
|
|
if ((valid_mask[f_const_addr[CW-1:LS]])
|
901 |
|
|
&&(cache_tags[f_const_addr[(CW-1):LS]]==f_const_addr[AW-1:CW]))
|
902 |
|
|
assert(f_const_insn == cache[f_const_addr[CW-1:0]]);
|
903 |
|
|
else if ((o_wb_cyc)&&(o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS])
|
904 |
|
|
&&(f_nacks > f_const_addr[LS-1:0]))
|
905 |
|
|
begin
|
906 |
|
|
assert(f_const_insn == cache[f_const_addr[CW-1:0]]);
|
907 |
|
|
end
|
908 |
|
|
|
909 |
|
|
always @(*)
|
910 |
|
|
if (o_wb_cyc)
|
911 |
|
|
assert(wraddr[CW-1:LS] == o_wb_addr[CW-1:LS]);
|
912 |
|
|
|
913 |
|
|
always @(*)
|
914 |
|
|
if (!f_const_addr[AW])
|
915 |
|
|
assert((!illegal_valid)
|
916 |
|
|
||(illegal_cache != f_const_addr[AW-1:LS]));
|
917 |
|
|
else
|
918 |
|
|
assert((cache_tags[f_const_addr[CW-1:LS]]!=f_const_addr[AW-1:CW])
|
919 |
|
|
||(!valid_mask[f_const_addr[CW-1:LS]]));
|
920 |
|
|
|
921 |
|
|
always @(*)
|
922 |
|
|
if ((f_this_line)&&(o_wb_cyc))
|
923 |
|
|
begin
|
924 |
|
|
if (f_const_addr[AW])
|
925 |
|
|
assume(!i_wb_ack);
|
926 |
|
|
else
|
927 |
|
|
assume(!i_wb_err);
|
928 |
|
|
|
929 |
|
|
if ((f_this_ack)&&(i_wb_ack))
|
930 |
|
|
assume(f_this_data);
|
931 |
|
|
end
|
932 |
|
|
|
933 |
|
|
always @(*)
|
934 |
|
|
if ((f_this_line)&&(!f_const_addr[AW]))
|
935 |
|
|
assume(!i_wb_err);
|
936 |
|
|
|
937 |
|
|
always @(*)
|
938 |
|
|
if (!f_const_addr[AW])
|
939 |
|
|
assume((!valid_mask[f_const_addr[CW-1:LS]])
|
940 |
|
|
||(cache_tags[f_const_addr[CW-1:LS]] != f_const_addr[AW-1:CW]));
|
941 |
|
|
`endif
|
942 |
|
|
|
943 |
|
|
//
|
944 |
|
|
//
|
945 |
|
|
// Cover properties
|
946 |
|
|
//
|
947 |
|
|
//
|
948 |
|
|
reg f_valid_legal;
|
949 |
|
|
always @(*)
|
950 |
|
|
f_valid_legal = o_valid && (!o_illegal);
|
951 |
|
|
always @(posedge i_clk) // Trace 0
|
952 |
|
|
cover((o_valid)&&( o_illegal));
|
953 |
|
|
always @(posedge i_clk) // Trace 1
|
954 |
|
|
cover(f_valid_legal);
|
955 |
|
|
always @(posedge i_clk) // Trace 2
|
956 |
|
|
cover((f_valid_legal)
|
957 |
|
|
&&($past(!o_valid && !i_new_pc))
|
958 |
|
|
&&($past(i_new_pc,2)));
|
959 |
|
|
always @(posedge i_clk) // Trace 3
|
960 |
|
|
cover((f_valid_legal)&&($past(i_stall_n))&&($past(i_new_pc)));
|
961 |
|
|
always @(posedge i_clk) // Trace 4
|
962 |
|
|
cover((f_valid_legal)&&($past(f_valid_legal && i_stall_n)));
|
963 |
|
|
always @(posedge i_clk) // Trace 5
|
964 |
|
|
cover((f_valid_legal)
|
965 |
|
|
&&($past(f_valid_legal && i_stall_n))
|
966 |
|
|
&&($past(f_valid_legal && i_stall_n,2))
|
967 |
|
|
&&($past(f_valid_legal && i_stall_n,3)));
|
968 |
|
|
always @(posedge i_clk) // Trace 6
|
969 |
|
|
cover((f_valid_legal)
|
970 |
|
|
&&($past(f_valid_legal && i_stall_n))
|
971 |
|
|
&&($past(f_valid_legal && i_stall_n,2))
|
972 |
|
|
&&($past(!o_illegal && i_stall_n && i_new_pc,3))
|
973 |
|
|
&&($past(f_valid_legal && i_stall_n,4))
|
974 |
|
|
&&($past(f_valid_legal && i_stall_n,5))
|
975 |
|
|
&&($past(f_valid_legal && i_stall_n,6)));
|
976 |
|
|
|
977 |
|
|
`endif // FORMAL
|
978 |
69 |
dgisselq |
endmodule
|