OpenCores
URL https://opencores.org/ocsvn/dblclockfft/dblclockfft/trunk

Subversion Repositories dblclockfft

[/] [dblclockfft/] [trunk/] [rtl/] [fftstage.v] - Blame information for rev 39

Details | Compare with Previous | View Log

Line No. Rev Author Line
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.