1 |
36 |
dgisselq |
////////////////////////////////////////////////////////////////////////////////
|
2 |
|
|
//
|
3 |
|
|
// Filename: fftstage.v
|
4 |
|
|
//
|
5 |
|
|
// Project: A General Purpose Pipelined FFT Implementation
|
6 |
|
|
//
|
7 |
|
|
// Purpose: This file is (almost) a Verilog source file. It is meant to
|
8 |
|
|
// be used by a FFT core compiler to generate FFTs which may be
|
9 |
|
|
// used as part of an FFT core. Specifically, this file encapsulates
|
10 |
|
|
// the options of an FFT-stage. For any 2^N length FFT, there shall be
|
11 |
|
|
// (N-1) of these stages.
|
12 |
|
|
//
|
13 |
|
|
//
|
14 |
|
|
// Operation:
|
15 |
|
|
// Given a stream of values, operate upon them as though they were
|
16 |
|
|
// value pairs, x[n] and x[n+N/2]. The stream begins when n=0, and ends
|
17 |
|
|
// when n=N/2-1 (i.e. there's a full set of N values). When the value
|
18 |
|
|
// x[0] enters, the synchronization input, i_sync, must be true as well.
|
19 |
|
|
//
|
20 |
|
|
// For this stream, produce outputs
|
21 |
|
|
// y[n ] = x[n] + x[n+N/2], and
|
22 |
|
|
// y[n+N/2] = (x[n] - x[n+N/2]) * c[n],
|
23 |
|
|
// where c[n] is a complex coefficient found in the
|
24 |
|
|
// external memory file COEFFILE.
|
25 |
|
|
// When y[0] is output, a synchronization bit o_sync will be true as
|
26 |
|
|
// well, otherwise it will be zero.
|
27 |
|
|
//
|
28 |
|
|
// Most of the work to do this is done within the butterfly, whether the
|
29 |
|
|
// hardware accelerated butterfly (uses a DSP) or not.
|
30 |
|
|
//
|
31 |
|
|
// Creator: Dan Gisselquist, Ph.D.
|
32 |
|
|
// Gisselquist Technology, LLC
|
33 |
|
|
//
|
34 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
35 |
|
|
//
|
36 |
|
|
// Copyright (C) 2015-2018, Gisselquist Technology, LLC
|
37 |
|
|
//
|
38 |
39 |
dgisselq |
// This file is part of the general purpose pipelined FFT project.
|
39 |
36 |
dgisselq |
//
|
40 |
39 |
dgisselq |
// The pipelined FFT project is free software (firmware): you can redistribute
|
41 |
|
|
// it and/or modify it under the terms of the GNU Lesser General Public License
|
42 |
|
|
// as published by the Free Software Foundation, either version 3 of the
|
43 |
|
|
// License, or (at your option) any later version.
|
44 |
36 |
dgisselq |
//
|
45 |
39 |
dgisselq |
// The pipelined FFT project is distributed in the hope that it will be useful,
|
46 |
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
47 |
|
|
// MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
48 |
|
|
// General Public License for more details.
|
49 |
|
|
//
|
50 |
|
|
// You should have received a copy of the GNU Lesser General Public License
|
51 |
|
|
// along with this program. (It's in the $(ROOT)/doc directory. Run make
|
52 |
|
|
// with no target there if the PDF file isn't present.) If not, see
|
53 |
36 |
dgisselq |
// <http://www.gnu.org/licenses/> for a copy.
|
54 |
|
|
//
|
55 |
39 |
dgisselq |
// License: LGPL, v3, as defined and found on www.gnu.org,
|
56 |
|
|
// http://www.gnu.org/licenses/lgpl.html
|
57 |
36 |
dgisselq |
//
|
58 |
|
|
//
|
59 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
60 |
|
|
//
|
61 |
|
|
//
|
62 |
|
|
`default_nettype none
|
63 |
|
|
//
|
64 |
|
|
module fftstage(i_clk, i_reset, i_ce, i_sync, i_data, o_data, o_sync);
|
65 |
|
|
parameter IWIDTH=15,CWIDTH=20,OWIDTH=16;
|
66 |
|
|
// Parameters specific to the core that should be changed when this
|
67 |
|
|
// core is built ... Note that the minimum LGSPAN (the base two log
|
68 |
|
|
// of the span, or the base two log of the current FFT size) is 3.
|
69 |
|
|
// Smaller spans (i.e. the span of 2) must use the dbl laststage module.
|
70 |
39 |
dgisselq |
parameter LGSPAN=10, BFLYSHIFT=0; // LGWIDTH=11
|
71 |
36 |
dgisselq |
parameter [0:0] OPT_HWMPY = 1;
|
72 |
|
|
// Clocks per CE. If your incoming data rate is less than 50% of your
|
73 |
|
|
// clock speed, you can set CKPCE to 2'b10, make sure there's at least
|
74 |
|
|
// one clock between cycles when i_ce is high, and then use two
|
75 |
|
|
// multiplies instead of three. Setting CKPCE to 2'b11, and insisting
|
76 |
|
|
// on at least two clocks with i_ce low between cycles with i_ce high,
|
77 |
|
|
// then the hardware optimized butterfly code will used one multiply
|
78 |
|
|
// instead of two.
|
79 |
|
|
parameter CKPCE = 1;
|
80 |
|
|
// The COEFFILE parameter contains the name of the file containing the
|
81 |
|
|
// FFT twiddle factors
|
82 |
39 |
dgisselq |
parameter COEFFILE="cmem_2048.hex";
|
83 |
36 |
dgisselq |
|
84 |
|
|
`ifdef VERILATOR
|
85 |
|
|
parameter [0:0] ZERO_ON_IDLE = 1'b0;
|
86 |
|
|
`else
|
87 |
|
|
localparam [0:0] ZERO_ON_IDLE = 1'b0;
|
88 |
|
|
`endif // VERILATOR
|
89 |
|
|
|
90 |
39 |
dgisselq |
input wire i_clk, i_reset, i_ce, i_sync;
|
91 |
|
|
input wire [(2*IWIDTH-1):0] i_data;
|
92 |
36 |
dgisselq |
output reg [(2*OWIDTH-1):0] o_data;
|
93 |
|
|
output reg o_sync;
|
94 |
|
|
|
95 |
39 |
dgisselq |
// I am using the prefixes
|
96 |
|
|
// ib_* to reference the inputs to the butterfly, and
|
97 |
|
|
// ob_* to reference the outputs from the butterfly
|
98 |
36 |
dgisselq |
reg wait_for_sync;
|
99 |
|
|
reg [(2*IWIDTH-1):0] ib_a, ib_b;
|
100 |
|
|
reg [(2*CWIDTH-1):0] ib_c;
|
101 |
|
|
reg ib_sync;
|
102 |
|
|
|
103 |
|
|
reg b_started;
|
104 |
|
|
wire ob_sync;
|
105 |
|
|
wire [(2*OWIDTH-1):0] ob_a, ob_b;
|
106 |
|
|
|
107 |
|
|
// cmem is defined as an array of real and complex values,
|
108 |
|
|
// where the top CWIDTH bits are the real value and the bottom
|
109 |
|
|
// CWIDTH bits are the imaginary value.
|
110 |
|
|
//
|
111 |
|
|
// cmem[i] = { (2^(CWIDTH-2)) * cos(2*pi*i/(2^LGWIDTH)),
|
112 |
|
|
// (2^(CWIDTH-2)) * sin(2*pi*i/(2^LGWIDTH)) };
|
113 |
|
|
//
|
114 |
|
|
reg [(2*CWIDTH-1):0] cmem [0:((1<<LGSPAN)-1)];
|
115 |
39 |
dgisselq |
`ifdef FORMAL
|
116 |
|
|
// Let the formal tool pick the coefficients
|
117 |
|
|
`else
|
118 |
36 |
dgisselq |
initial $readmemh(COEFFILE,cmem);
|
119 |
|
|
|
120 |
39 |
dgisselq |
`endif
|
121 |
|
|
|
122 |
36 |
dgisselq |
reg [(LGSPAN):0] iaddr;
|
123 |
|
|
reg [(2*IWIDTH-1):0] imem [0:((1<<LGSPAN)-1)];
|
124 |
|
|
|
125 |
39 |
dgisselq |
reg [LGSPAN:0] oaddr;
|
126 |
36 |
dgisselq |
reg [(2*OWIDTH-1):0] omem [0:((1<<LGSPAN)-1)];
|
127 |
|
|
|
128 |
|
|
initial wait_for_sync = 1'b1;
|
129 |
|
|
initial iaddr = 0;
|
130 |
|
|
always @(posedge i_clk)
|
131 |
39 |
dgisselq |
if (i_reset)
|
132 |
36 |
dgisselq |
begin
|
133 |
39 |
dgisselq |
wait_for_sync <= 1'b1;
|
134 |
|
|
iaddr <= 0;
|
135 |
36 |
dgisselq |
end else if ((i_ce)&&((!wait_for_sync)||(i_sync)))
|
136 |
|
|
begin
|
137 |
|
|
//
|
138 |
|
|
// First step: Record what we're not ready to use yet
|
139 |
|
|
//
|
140 |
|
|
iaddr <= iaddr + { {(LGSPAN){1'b0}}, 1'b1 };
|
141 |
|
|
wait_for_sync <= 1'b0;
|
142 |
|
|
end
|
143 |
|
|
always @(posedge i_clk) // Need to make certain here that we don't read
|
144 |
|
|
if ((i_ce)&&(!iaddr[LGSPAN])) // and write the same address on
|
145 |
|
|
imem[iaddr[(LGSPAN-1):0]] <= i_data; // the same clk
|
146 |
|
|
|
147 |
|
|
//
|
148 |
|
|
// Now, we have all the inputs, so let's feed the butterfly
|
149 |
|
|
//
|
150 |
39 |
dgisselq |
// ib_sync is the synchronization bit to the butterfly. It will
|
151 |
|
|
// be tracked within the butterfly, and used to create the o_sync
|
152 |
|
|
// value when the results from this output are produced
|
153 |
36 |
dgisselq |
initial ib_sync = 1'b0;
|
154 |
|
|
always @(posedge i_clk)
|
155 |
|
|
if (i_reset)
|
156 |
|
|
ib_sync <= 1'b0;
|
157 |
|
|
else if (i_ce)
|
158 |
|
|
begin
|
159 |
|
|
// Set the sync to true on the very first
|
160 |
|
|
// valid input in, and hence on the very
|
161 |
|
|
// first valid data out per FFT.
|
162 |
|
|
ib_sync <= (iaddr==(1<<(LGSPAN)));
|
163 |
|
|
end
|
164 |
|
|
|
165 |
39 |
dgisselq |
// Read the values from our input memory, and use them to feed first of two
|
166 |
|
|
// butterfly inputs
|
167 |
36 |
dgisselq |
always @(posedge i_clk)
|
168 |
|
|
if (i_ce)
|
169 |
|
|
begin
|
170 |
|
|
// One input from memory, ...
|
171 |
|
|
ib_a <= imem[iaddr[(LGSPAN-1):0]];
|
172 |
|
|
// One input clocked in from the top
|
173 |
|
|
ib_b <= i_data;
|
174 |
|
|
// and the coefficient or twiddle factor
|
175 |
|
|
ib_c <= cmem[iaddr[(LGSPAN-1):0]];
|
176 |
|
|
end
|
177 |
|
|
|
178 |
|
|
// The idle register is designed to keep track of when an input
|
179 |
|
|
// to the butterfly is important and going to be used. It's used
|
180 |
|
|
// in a flag following, so that when useful values are placed
|
181 |
|
|
// into the butterfly they'll be non-zero (idle=0), otherwise when
|
182 |
|
|
// the inputs to the butterfly are irrelevant and will be ignored,
|
183 |
|
|
// then (idle=1) those inputs will be set to zero. This
|
184 |
|
|
// functionality is not designed to be used in operation, but only
|
185 |
|
|
// within a Verilator simulation context when chasing a bug.
|
186 |
|
|
// In this limited environment, the non-zero answers will stand
|
187 |
|
|
// in a trace making it easier to highlight a bug.
|
188 |
|
|
reg idle;
|
189 |
|
|
generate if (ZERO_ON_IDLE)
|
190 |
|
|
begin
|
191 |
|
|
initial idle = 1;
|
192 |
|
|
always @(posedge i_clk)
|
193 |
|
|
if (i_reset)
|
194 |
|
|
idle <= 1'b1;
|
195 |
|
|
else if (i_ce)
|
196 |
|
|
idle <= (!iaddr[LGSPAN])&&(!wait_for_sync);
|
197 |
|
|
|
198 |
|
|
end else begin
|
199 |
|
|
|
200 |
|
|
always @(*) idle = 0;
|
201 |
|
|
|
202 |
|
|
end endgenerate
|
203 |
|
|
|
204 |
39 |
dgisselq |
// For the formal proof, we'll assume the outputs of hwbfly and/or
|
205 |
|
|
// butterfly, rather than actually calculating them. This will simplify
|
206 |
|
|
// the proof and (if done properly) will be equivalent. Be careful of
|
207 |
|
|
// defining FORMAL if you want the full logic!
|
208 |
|
|
`ifndef FORMAL
|
209 |
|
|
//
|
210 |
36 |
dgisselq |
generate if (OPT_HWMPY)
|
211 |
|
|
begin : HWBFLY
|
212 |
|
|
hwbfly #(.IWIDTH(IWIDTH),.CWIDTH(CWIDTH),.OWIDTH(OWIDTH),
|
213 |
|
|
.CKPCE(CKPCE), .SHIFT(BFLYSHIFT))
|
214 |
|
|
bfly(i_clk, i_reset, i_ce, (idle)?0:ib_c,
|
215 |
|
|
(idle || (!i_ce)) ? 0:ib_a,
|
216 |
|
|
(idle || (!i_ce)) ? 0:ib_b,
|
217 |
|
|
(ib_sync)&&(i_ce),
|
218 |
|
|
ob_a, ob_b, ob_sync);
|
219 |
|
|
end else begin : FWBFLY
|
220 |
|
|
butterfly #(.IWIDTH(IWIDTH),.CWIDTH(CWIDTH),.OWIDTH(OWIDTH),
|
221 |
|
|
.CKPCE(CKPCE),.SHIFT(BFLYSHIFT))
|
222 |
|
|
bfly(i_clk, i_reset, i_ce,
|
223 |
|
|
(idle||(!i_ce))?0:ib_c,
|
224 |
|
|
(idle||(!i_ce))?0:ib_a,
|
225 |
|
|
(idle||(!i_ce))?0:ib_b,
|
226 |
|
|
(ib_sync&&i_ce),
|
227 |
|
|
ob_a, ob_b, ob_sync);
|
228 |
|
|
end endgenerate
|
229 |
39 |
dgisselq |
`endif
|
230 |
36 |
dgisselq |
|
231 |
|
|
//
|
232 |
|
|
// Next step: recover the outputs from the butterfly
|
233 |
|
|
//
|
234 |
39 |
dgisselq |
// The first output can go immediately to the output of this routine
|
235 |
|
|
// The second output must wait until this time in the idle cycle
|
236 |
|
|
// oaddr is the output memory address, keeping track of where we are
|
237 |
|
|
// in this output cycle.
|
238 |
|
|
initial oaddr = 0;
|
239 |
36 |
dgisselq |
initial o_sync = 0;
|
240 |
|
|
initial b_started = 0;
|
241 |
|
|
always @(posedge i_clk)
|
242 |
39 |
dgisselq |
if (i_reset)
|
243 |
36 |
dgisselq |
begin
|
244 |
39 |
dgisselq |
oaddr <= 0;
|
245 |
|
|
o_sync <= 0;
|
246 |
|
|
// b_started will be true once we've seen the first ob_sync
|
247 |
36 |
dgisselq |
b_started <= 0;
|
248 |
|
|
end else if (i_ce)
|
249 |
|
|
begin
|
250 |
39 |
dgisselq |
o_sync <= (!oaddr[LGSPAN])?ob_sync : 1'b0;
|
251 |
36 |
dgisselq |
if (ob_sync||b_started)
|
252 |
39 |
dgisselq |
oaddr <= oaddr + 1'b1;
|
253 |
|
|
if ((ob_sync)&&(!oaddr[LGSPAN]))
|
254 |
|
|
// If b_started is true, then a butterfly output is available
|
255 |
36 |
dgisselq |
b_started <= 1'b1;
|
256 |
|
|
end
|
257 |
|
|
|
258 |
39 |
dgisselq |
reg [(LGSPAN-1):0] nxt_oaddr;
|
259 |
|
|
reg [(2*OWIDTH-1):0] pre_ovalue;
|
260 |
36 |
dgisselq |
always @(posedge i_clk)
|
261 |
|
|
if (i_ce)
|
262 |
39 |
dgisselq |
nxt_oaddr[0] <= oaddr[0];
|
263 |
|
|
generate if (LGSPAN>1)
|
264 |
36 |
dgisselq |
begin
|
265 |
39 |
dgisselq |
|
266 |
|
|
always @(posedge i_clk)
|
267 |
|
|
if (i_ce)
|
268 |
|
|
nxt_oaddr[LGSPAN-1:1] <= oaddr[LGSPAN-1:1] + 1'b1;
|
269 |
|
|
|
270 |
|
|
end endgenerate
|
271 |
|
|
|
272 |
|
|
// Only write to the memory on the first half of the outputs
|
273 |
|
|
// We'll use the memory value on the second half of the outputs
|
274 |
36 |
dgisselq |
always @(posedge i_clk)
|
275 |
39 |
dgisselq |
if ((i_ce)&&(!oaddr[LGSPAN]))
|
276 |
|
|
omem[oaddr[(LGSPAN-1):0]] <= ob_b;
|
277 |
|
|
|
278 |
|
|
always @(posedge i_clk)
|
279 |
36 |
dgisselq |
if (i_ce)
|
280 |
39 |
dgisselq |
pre_ovalue <= omem[nxt_oaddr[(LGSPAN-1):0]];
|
281 |
36 |
dgisselq |
|
282 |
|
|
always @(posedge i_clk)
|
283 |
|
|
if (i_ce)
|
284 |
39 |
dgisselq |
o_data <= (!oaddr[LGSPAN]) ? ob_a : pre_ovalue;
|
285 |
36 |
dgisselq |
|
286 |
39 |
dgisselq |
`ifdef FORMAL
|
287 |
|
|
// An arbitrary processing delay from butterfly input to
|
288 |
|
|
// butterfly output(s)
|
289 |
|
|
(* anyconst *) reg [LGSPAN:0] f_mpydelay;
|
290 |
|
|
always @(*)
|
291 |
|
|
assume(f_mpydelay > 1);
|
292 |
|
|
|
293 |
|
|
reg f_past_valid;
|
294 |
|
|
initial f_past_valid = 1'b0;
|
295 |
|
|
always @(posedge i_clk)
|
296 |
|
|
f_past_valid <= 1'b1;
|
297 |
|
|
|
298 |
|
|
always @(posedge i_clk)
|
299 |
|
|
if ((!f_past_valid)||($past(i_reset)))
|
300 |
|
|
begin
|
301 |
|
|
assert(iaddr == 0);
|
302 |
|
|
assert(wait_for_sync);
|
303 |
|
|
assert(o_sync == 0);
|
304 |
|
|
assert(oaddr == 0);
|
305 |
|
|
assert(!b_started);
|
306 |
|
|
assert(!o_sync);
|
307 |
|
|
end
|
308 |
|
|
|
309 |
|
|
/////////////////////////////////////////
|
310 |
|
|
//
|
311 |
|
|
// Formally verify the input half, from the inputs to this module
|
312 |
|
|
// to the inputs of the butterfly
|
313 |
|
|
//
|
314 |
|
|
/////////////////////////////////////////
|
315 |
|
|
//
|
316 |
|
|
// Let's verify a specific set of inputs
|
317 |
|
|
(* anyconst *) reg [LGSPAN:0] f_addr;
|
318 |
|
|
reg [2*IWIDTH-1:0] f_left, f_right;
|
319 |
|
|
wire [LGSPAN:0] f_next_addr;
|
320 |
|
|
|
321 |
|
|
always @(posedge i_clk)
|
322 |
|
|
if ((!$past(i_ce))&&(!$past(i_ce,2))&&(!$past(i_ce,3))&&(!$past(i_ce,4)))
|
323 |
|
|
assume(!i_ce);
|
324 |
|
|
|
325 |
|
|
always @(*)
|
326 |
|
|
assume(f_addr[LGSPAN]==1'b0);
|
327 |
|
|
|
328 |
|
|
assign f_next_addr = f_addr + 1'b1;
|
329 |
|
|
|
330 |
|
|
always @(posedge i_clk)
|
331 |
|
|
if ((i_ce)&&(iaddr[LGSPAN:0] == f_addr))
|
332 |
|
|
f_left <= i_data;
|
333 |
|
|
|
334 |
|
|
always @(*)
|
335 |
|
|
if (wait_for_sync)
|
336 |
|
|
assert(iaddr == 0);
|
337 |
|
|
|
338 |
|
|
wire [LGSPAN:0] f_last_addr = iaddr - 1'b1;
|
339 |
|
|
|
340 |
|
|
always @(posedge i_clk)
|
341 |
|
|
if ((!wait_for_sync)&&(f_last_addr >= { 1'b0, f_addr[LGSPAN-1:0]}))
|
342 |
|
|
assert(f_left == imem[f_addr[LGSPAN-1:0]]);
|
343 |
|
|
|
344 |
|
|
always @(posedge i_clk)
|
345 |
|
|
if ((i_ce)&&(iaddr == { 1'b1, f_addr[LGSPAN-1:0]}))
|
346 |
|
|
f_right <= i_data;
|
347 |
|
|
|
348 |
|
|
always @(posedge i_clk)
|
349 |
|
|
if ((i_ce)&&(!wait_for_sync)&&(f_last_addr == { 1'b1, f_addr[LGSPAN-1:0]}))
|
350 |
|
|
begin
|
351 |
|
|
assert(ib_a == f_left);
|
352 |
|
|
assert(ib_b == f_right);
|
353 |
|
|
assert(ib_c == cmem[f_addr[LGSPAN-1:0]]);
|
354 |
|
|
end
|
355 |
|
|
|
356 |
|
|
/////////////////////////////////////////
|
357 |
|
|
//
|
358 |
|
|
// Formally verify the output half, from the output of the butterfly
|
359 |
|
|
// to the outputs of this module
|
360 |
|
|
//
|
361 |
|
|
/////////////////////////////////////////
|
362 |
|
|
reg [2*OWIDTH-1:0] f_oleft, f_oright;
|
363 |
|
|
reg [LGSPAN:0] f_oaddr;
|
364 |
|
|
wire [LGSPAN:0] f_oaddr_m1 = f_oaddr - 1'b1;
|
365 |
|
|
|
366 |
|
|
always @(*)
|
367 |
|
|
f_oaddr = iaddr - f_mpydelay + {1'b1,{(LGSPAN-1){1'b0}}};
|
368 |
|
|
|
369 |
|
|
assign f_oaddr_m1 = f_oaddr - 1'b1;
|
370 |
|
|
|
371 |
|
|
reg f_output_active;
|
372 |
|
|
initial f_output_active = 1'b0;
|
373 |
|
|
always @(posedge i_clk)
|
374 |
|
|
if (i_reset)
|
375 |
|
|
f_output_active <= 1'b0;
|
376 |
|
|
else if ((i_ce)&&(ob_sync))
|
377 |
|
|
f_output_active <= 1'b1;
|
378 |
|
|
|
379 |
|
|
always @(*)
|
380 |
|
|
assert(f_output_active == b_started);
|
381 |
|
|
|
382 |
|
|
always @(*)
|
383 |
|
|
if (wait_for_sync)
|
384 |
|
|
assert(!f_output_active);
|
385 |
|
|
|
386 |
|
|
always @(*)
|
387 |
|
|
if (f_output_active)
|
388 |
|
|
assert(oaddr == f_oaddr);
|
389 |
|
|
else
|
390 |
|
|
assert(oaddr == 0);
|
391 |
|
|
|
392 |
|
|
always @(*)
|
393 |
|
|
if (wait_for_sync)
|
394 |
|
|
assume(!ob_sync);
|
395 |
|
|
|
396 |
|
|
always @(*)
|
397 |
|
|
assume(ob_sync == (f_oaddr == 0));
|
398 |
|
|
|
399 |
|
|
always @(posedge i_clk)
|
400 |
|
|
if ((f_past_valid)&&(!$past(i_ce)))
|
401 |
|
|
begin
|
402 |
|
|
assume($stable(ob_a));
|
403 |
|
|
assume($stable(ob_b));
|
404 |
|
|
end
|
405 |
|
|
|
406 |
|
|
initial f_oleft = 0;
|
407 |
|
|
initial f_oright = 0;
|
408 |
|
|
always @(posedge i_clk)
|
409 |
|
|
if ((i_ce)&&(f_oaddr == f_addr))
|
410 |
|
|
begin
|
411 |
|
|
f_oleft <= ob_a;
|
412 |
|
|
f_oright <= ob_b;
|
413 |
|
|
end
|
414 |
|
|
|
415 |
|
|
always @(posedge i_clk)
|
416 |
|
|
if ((f_output_active)&&(f_oaddr_m1 >= { 1'b0, f_addr[LGSPAN-1:0]}))
|
417 |
|
|
assert(omem[f_addr[LGSPAN-1:0]] == f_oright);
|
418 |
|
|
|
419 |
|
|
always @(posedge i_clk)
|
420 |
|
|
if ((i_ce)&&(f_oaddr_m1 == 0)&&(f_output_active))
|
421 |
|
|
assert(o_sync);
|
422 |
|
|
else if ((i_ce)||(!f_output_active))
|
423 |
|
|
assert(!o_sync);
|
424 |
|
|
|
425 |
|
|
always @(posedge i_clk)
|
426 |
|
|
if ((i_ce)&&(f_output_active)&&(f_oaddr_m1 == f_addr))
|
427 |
|
|
assert(o_data == f_oleft);
|
428 |
|
|
always @(posedge i_clk)
|
429 |
|
|
if ((i_ce)&&(f_output_active)&&(f_oaddr[LGSPAN])
|
430 |
|
|
&&(f_oaddr[LGSPAN-1:0] == f_addr[LGSPAN-1:0]))
|
431 |
|
|
assert(pre_ovalue == f_oright);
|
432 |
|
|
always @(posedge i_clk)
|
433 |
|
|
if ((i_ce)&&(f_output_active)&&(f_oaddr_m1[LGSPAN])
|
434 |
|
|
&&(f_oaddr_m1[LGSPAN-1:0] == f_addr[LGSPAN-1:0]))
|
435 |
|
|
assert(o_data == f_oright);
|
436 |
|
|
|
437 |
|
|
`endif
|
438 |
36 |
dgisselq |
endmodule
|