URL
https://opencores.org/ocsvn/dblclockfft/dblclockfft/trunk
Subversion Repositories dblclockfft
Compare Revisions
- This comparison shows the changes necessary to convert path
/dblclockfft/trunk/bench/cpp
- from Rev 35 to Rev 36
- ↔ Reverse comparison
Rev 35 → Rev 36
/fftstage_o2048_tb.cpp
File deleted
/dblrev_tb.cpp
File deleted
/dblstage_tb.cpp
File deleted
/Makefile
6,15 → 6,14
## |
## Purpose: This programs the build process for the test benches |
## associated with the double clocked FFT project. These |
## test benches are designed for the size and arguments of the |
## FFT as given by the Makefile in the trunk/sw directory, |
## although they shouldn't be too difficult to modify for |
## other FFT parameters. |
## test benches are designed for the size and arguments of the FFT as |
## given by the Makefile in the trunk/sw directory, although they shouldn't |
## be too difficult to modify for other FFT parameters. |
## |
## Please note that running these test benches requires access |
## to the *cmem_*.hex files found in trunk/sw/fft-core. I |
## usually soft link them into this directory, but such linking |
## is not currently part of this makefile or the build scripts. |
## Please note that running these test benches requires access to the |
## *cmem_*.hex files found in trunk/rtl. I usually soft link |
## them into this directory, but such linking is not currently part of |
## this makefile or the build scripts. |
## |
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
21,7 → 20,7
## |
##########################################################################/ |
## |
## Copyright (C) 2015, Gisselquist Technology, LLC |
## Copyright (C) 2015,2018 Gisselquist Technology, LLC |
## |
## This program is free software (firmware): you can redistribute it and/or |
## modify it under the terms of the GNU General Public License as published |
43,10 → 42,11
## |
## |
##########################################################################/ |
all: mpy_tb dblrev_tb dblstage_tb qtrstage_tb fft_tb test |
all: mpy_tb bitreverse_tb hwbfly_tb butterfly_tb fftstage_tb fft_tb |
all: qtrstage_tb laststage_tb test |
|
OBJDR:= ../../sw/fft-core/obj_dir |
VSRCD = ../../sw/fft-core |
OBJDR:= ../../rtl/obj_dir |
VSRCD = ../../rtl |
TBODR:= ../rtl/obj_dir |
ifneq ($(VERILATOR_ROOT),) |
VERILATOR:=$(VERILATOR_ROOT)/bin/verilator |
60,24 → 60,25
VINC := -I$(VROOT)/include -I$(OBJDR)/ -I$(TBODR)/ |
# MPYLB:= $(OBJDR)/Vshiftaddmpy__ALL.a |
MPYLB:= $(OBJDR)/Vlongbimpy__ALL.a |
DBLRV:= $(OBJDR)/Vdblreverse__ALL.a |
DBLSG:= $(OBJDR)/Vdblstage__ALL.a |
BTREV:= $(OBJDR)/Vbitreverse__ALL.a |
STAGE:= $(OBJDR)/Vfftstage__ALL.a |
QTRSG:= $(OBJDR)/Vqtrstage__ALL.a |
LSTSG:= $(OBJDR)/Vlaststage__ALL.a |
BFLYL:= $(OBJDR)/Vbutterfly__ALL.a |
HWBFY:= $(OBJDR)/Vhwbfly__ALL.a |
FFTLB:= $(OBJDR)/Vfftmain__ALL.a |
IFTLB:= $(TBODR)/Vifft_tb__ALL.a |
STGLB:= $(OBJDR)/Vfftstage_o2048__ALL.a |
STGLB:= $(OBJDR)/Vfftstage__ALL.a |
VSRCS:= $(VROOT)/include/verilated.cpp $(VROOT)/include/verilated_vcd_c.cpp |
|
mpy_tb: mpy_tb.cpp fftsize.h twoc.h $(MPYLB) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(MPYLB) $(VSRCS) -o $@ |
|
dblrev_tb: dblrev_tb.cpp twoc.cpp twoc.h fftsize.h $(DBLRV) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(DBLRV) $(VSRCS) -o $@ |
bitreverse_tb: bitreverse_tb.cpp twoc.cpp twoc.h fftsize.h $(BTREV) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(BTREV) $(VSRCS) -o $@ |
|
dblstage_tb: dblstage_tb.cpp twoc.cpp twoc.h $(DBLSG) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(DBLSG) $(VSRCS) -o $@ |
laststage_tb: laststage_tb.cpp twoc.cpp twoc.h $(LSTSG) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(LSTSG) $(VSRCS) -o $@ |
|
qtrstage_tb: qtrstage_tb.cpp twoc.cpp twoc.h $(QTRSG) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(QTRSG) $(VSRCS) -o $@ |
88,7 → 89,7
hwbfly_tb: hwbfly_tb.cpp twoc.cpp twoc.h $(HWBFY) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(HWBFY) $(VSRCS) -o $@ |
|
fftstage_o2048_tb: fftstage_o2048_tb.cpp twoc.cpp twoc.h $(STGLB) |
fftstage_tb: fftstage_tb.cpp twoc.cpp twoc.h $(STGLB) |
g++ -g $(VINC) $(VDEFS) $< twoc.cpp $(STGLB) $(VSRCS) -o $@ |
|
fft_tb: fft_tb.cpp twoc.cpp twoc.h fftsize.h $(FFTLB) |
112,22 → 113,22
ln -s $(VSRCD)/*.hex . |
|
.PHONY: test |
test: mpy_tb dblrev_tb dblstage_tb qtrstage_tb butterfly_tb fftstage_o2048_tb |
test: mpy_tb bitreverse_tb fftstage_tb qtrstage_tb butterfly_tb fftstage_tb |
test: fft_tb ifft_tb hwbfly_tb |
./mpy_tb |
./dblrev_tb |
./dblstage_tb |
./qtrstage_tb |
./bitreverse_tb |
./fftstage_tb |
echo ./qtrstage_tb |
./butterfly_tb |
./hwbfly_tb |
./fftstage_o2048_tb |
./fftstage_tb |
./fft_tb |
./ifft_tb |
|
.PHONY: clean |
clean: |
rm -f mpy_tb dblrev_tb dblstage_tb qtrstage_tb butterfly_tb |
rm -f fftstage_o2048_tb fft_tb ifft_tb hwbfly_tb |
rm -f mpy_tb bitreverse_tb fftstage_tb qtrstage_tb butterfly_tb |
rm -f fftstage_tb fft_tb ifft_tb hwbfly_tb |
rm -rf fft_tb.dbl ifft_tb.dbl |
rm -rf *cmem_*.hex |
|
/README.md
0,0 → 1,18
Here are the bench tests for the pipelined FFT. In general, there's a |
`*_tb.cpp` file corresponding to every unit within the FFT. Feel free to |
try them. |
|
Be aware, however, the [fft_tb](fft_tb.cpp) doesn't truly |
check for success--I just haven't gotten to the point of verifying that |
the FFT result is *close enough* to the right answer in spite of actually |
calculating the right answer. Instead, it creates a data file that can be |
read in Octave via [fft_tb.m](fft_tb.m). That will show the first test output. |
The second and subsequent outputs can be read via `k=k+1;` followed by calling |
[plottst](plottst.m). |
|
As another note (before I clean things up more), you'll need the `*.hex` files |
in the same directory as the one you call [fft_tb](fft_tb.cpp) or |
[fftstage_tb](fftstage_tb.cpp) from. |
|
I expect the IFFT will work: it's just an FFT with conjugate twiddle factors, |
although I haven't fully tested it yet. |
/bitreverse_tb.cpp
0,0 → 1,235
//////////////////////////////////////////////////////////////////////////// |
// |
// Filename: snglbrev_tb.cpp |
// |
// Project: A General Purpose Pipelined FFT Implementation |
// |
// Purpose: A test-bench for the bitreversal stage of the pipelined |
// FFT. This file may be run autonomously. If so, the last line |
// output will either read "SUCCESS" on success, or some other failure |
// message otherwise. |
// |
// This file depends upon verilator to both compile, run, and therefore |
// test either snglbrev.v or dblreverse.v--depending on whether or not the |
// FFT handles one or two inputs per clock respectively. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2018, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
#include "verilated.h" |
#include "verilated_vcd_c.h" |
|
#include "fftsize.h" |
#include "Vbitreverse.h" |
|
#define FFTBITS TST_DBLREVERSE_LGSIZE |
#define FFTSIZE (1<<(FFTBITS)) |
#define FFTMASK (FFTSIZE-1) |
#define DATALEN (1<<(FFTBITS+1)) |
#define DATAMSK (DATALEN-1) |
#define PAGEMSK (FFTSIZE) |
|
#ifdef NEW_VERILATOR |
#define VVAR(A) bitreverse__DOT_ ## A |
#else |
#define VVAR(A) v__DOT_ ## A |
#endif |
|
typedef Vbitreverse TSTCLASS; |
|
#define iaddr VVAR(_wraddr) |
#define in_reset VVAR(_in_reset) |
|
VerilatedVcdC *trace = NULL; |
uint64_t m_tickcount = 0; |
|
void tick(TSTCLASS *brev) { |
m_tickcount++; |
|
brev->i_clk = 0; |
brev->eval(); |
if (trace) trace->dump((uint64_t)(10ul*m_tickcount-2)); |
brev->i_clk = 1; |
brev->eval(); |
if (trace) trace->dump((uint64_t)(10ul*m_tickcount)); |
brev->i_clk = 0; |
brev->eval(); |
if (trace) { |
trace->dump((uint64_t)(10ul*m_tickcount+5)); |
trace->flush(); |
} |
|
brev->i_ce = 0; |
} |
|
void cetick(TSTCLASS *brev) { |
brev->i_ce = 1; |
tick(brev); |
if (rand()&1) { |
brev->i_ce = 1; |
tick(brev); |
} |
} |
|
void reset(TSTCLASS *brev) { |
brev->i_ce = 0; |
brev->i_reset = 1; |
tick(brev); |
brev->i_ce = 0; |
brev->i_reset = 0; |
tick(brev); |
} |
|
unsigned long bitrev(const int nbits, const unsigned long vl) { |
unsigned long r = 0; |
unsigned long val = vl; |
|
for(int k=0; k<nbits; k++) { |
r <<= 1; |
r |= (val & 1); |
val >>= 1; |
} |
|
return r; |
} |
|
int main(int argc, char **argv, char **envp) { |
Verilated::commandArgs(argc, argv); |
Verilated::traceEverOn(true); |
TSTCLASS *brev = new TSTCLASS; |
int syncd = 0; |
unsigned long datastore[DATALEN], dataidx=0; |
const int BREV_OFFSET = 0; |
|
trace = new VerilatedVcdC; |
brev->trace(trace, 99); |
trace->open("bitreverse_tb.vcd"); |
|
reset(brev); |
|
printf("FFTSIZE = %08x\n", FFTSIZE); |
printf("FFTMASK = %08x\n", FFTMASK); |
printf("DATALEN = %08x\n", DATALEN); |
printf("DATAMSK = %08x\n", DATAMSK); |
|
for(int k=0; k<4*(FFTSIZE); k++) { |
brev->i_ce = 1; |
#ifdef DBLCLKFFT |
brev->i_in_0 = 2*k; |
brev->i_in_1 = 2*k+1; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in_0; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in_1; |
#else |
brev->i_in = k; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in; |
#endif |
tick(brev); |
|
printf("k=%3d: IN = %6lx, OUT = %6lx, SYNC = %d\t(%2x) %d\n", |
k, brev->i_in, brev->o_out, brev->o_sync, |
brev->iaddr, brev->in_reset); |
|
if ((k>BREV_OFFSET)&&((BREV_OFFSET==(k&FFTMASK))?1:0) != brev->o_sync) { |
fprintf(stdout, "FAIL, BAD SYNC (k = %d > %d)\n", k, BREV_OFFSET); |
exit(EXIT_FAILURE); |
} else if (brev->o_sync) { |
syncd = 1; |
} |
if ((syncd)&&((brev->o_out&FFTMASK) != bitrev(FFTBITS, k-BREV_OFFSET))) { |
fprintf(stdout, "FAIL: BITREV.0 of k (%2x) = %2lx, not %2lx\n", |
k, brev->o_out, bitrev(FFTBITS, (k-BREV_OFFSET))); |
exit(EXIT_FAILURE); |
} |
} |
|
for(int k=0; k<4*(FFTSIZE); k++) { |
brev->i_ce = 1; |
#ifdef DBLCLKFFT |
brev->i_in_0 = rand() & 0x0ffffff; |
brev->i_in_1 = rand() & 0x0ffffff; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in_0; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in_1; |
#else |
brev->i_in = rand() & 0x0ffffff; |
datastore[(dataidx++)&(DATAMSK)] = brev->i_in; |
#endif |
tick(brev); |
|
#ifdef DBLCLKFFT |
printf("k=%3d: IN = %6lx : %6lx, OUT = %6lx : %6lx, SYNC = %d\n", |
k, brev->i_in_0, brev->i_in_1, |
brev->o_out_0, brev->o_out_1, brev->o_sync); |
#else |
printf("k=%3d: IN = %6lx, OUT = %6lx, SYNC = %d\n", |
k, brev->i_in, brev->o_out, brev->o_sync); |
#endif |
|
if (brev->o_sync) |
syncd = 1; |
#ifdef DBLCLKFFT |
if ((syncd)&&(brev->o_out_0 != datastore[(((dataidx-2-FFTSIZE)&PAGEMSK) + bitrev(FFTBITS, (dataidx-FFTSIZE-2)&FFTMASK))])) { |
fprintf(stdout, "FAIL: BITREV.0 of k (%2x) = %2lx, not %2lx (expected %lx -> %lx)\n", |
k, brev->o_out_0, |
datastore[(((dataidx-2-FFTSIZE)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-2)&FFTMASK))], |
(dataidx-2)&DATAMSK, |
(((dataidx-2)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-2)&FFTMASK))); |
// exit(-1); |
} |
|
if ((syncd)&&(brev->o_out_1 != datastore[(((dataidx-2-FFTSIZE)&PAGEMSK) + bitrev(FFTBITS, (dataidx-FFTSIZE-1)&FFTMASK))])) { |
fprintf(stdout, "FAIL: BITREV.1 of k (%2x) = %2lx, not %2lx (expected %lx)\n", |
k, brev->o_out_1, |
datastore[(((dataidx-2-FFTSIZE)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-1)&FFTMASK))], |
(((dataidx-1)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-1)&FFTMASK))); |
// exit(-1); |
} |
#else |
if ((syncd)&&(brev->o_out != datastore[ |
(((dataidx-1-FFTSIZE)&PAGEMSK) |
+ bitrev(FFTBITS, |
(dataidx-FFTSIZE-1)&FFTMASK))])) { |
fprintf(stdout, "FAIL: BITREV.0 of k (%2x) = %2lx, not %2lx (expected %lx -> %lx)\n", |
k, brev->o_out, |
datastore[(((dataidx-1-FFTSIZE)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-1)&FFTMASK))], |
(dataidx-2)&DATAMSK, |
(((dataidx-2)&PAGEMSK) |
+ bitrev(FFTBITS, (dataidx-FFTSIZE-1)&FFTMASK))); |
exit(EXIT_FAILURE); |
} |
#endif |
} |
|
delete brev; |
|
printf("SUCCESS!\n"); |
exit(0); |
} |
/butterfly_tb.cpp
4,13 → 4,13
// |
// Project: A Doubletime Pipelined FFT |
// |
// Purpose: A test-bench for the butterfly.v subfile of the double |
// clocked FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, |
// or some other failure message otherwise. |
// Purpose: A test-bench for the butterfly.v subfile of the generic |
// pipelined FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, or some |
// other failure message otherwise. |
// |
// This file depends upon verilator to both compile, run, and |
// therefore test butterfly.v |
// This file depends upon verilator to both compile, run, and therefore |
// test butterfly.v |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
17,7 → 17,7
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015,2018 Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
42,11 → 42,18
#include <stdio.h> |
#include <stdint.h> |
|
#include "fftsize.h" |
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "Vbutterfly.h" |
#include "verilated.h" |
#include "twoc.h" |
#include "fftsize.h" |
|
#ifdef NEW_VERILATOR |
#define VVAR(A) butterfly__DOT__ ## A |
#else |
#define VVAR(A) v__DOT_ ## A |
#endif |
|
#define IWIDTH TST_BUTTERFLY_IWIDTH |
#define CWIDTH TST_BUTTERFLY_CWIDTH |
#define OWIDTH TST_BUTTERFLY_OWIDTH |
55,24 → 62,55
class BFLY_TB { |
public: |
Vbutterfly *m_bfly; |
VerilatedVcdC *m_trace; |
unsigned long m_left[64], m_right[64]; |
bool m_aux[64]; |
int m_addr, m_lastaux, m_offset; |
bool m_syncd, m_waiting_for_sync_input; |
uint64_t m_tickcount; |
|
BFLY_TB(void) { |
Verilated::traceEverOn(true); |
m_trace = NULL; |
m_bfly = new Vbutterfly; |
m_addr = 0; |
m_syncd = 0; |
m_tickcount = 0; |
m_waiting_for_sync_input = true; |
} |
|
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_bfly->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_lastaux = m_bfly->o_aux; |
m_bfly->i_clk = 0; |
m_bfly->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount-2)); |
m_bfly->i_clk = 1; |
m_bfly->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount)); |
m_bfly->i_clk = 0; |
m_bfly->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul*m_tickcount+5)); |
m_trace->flush(); |
} |
|
if ((!m_syncd)&&(m_bfly->o_aux)) |
m_offset = m_addr; |
79,14 → 117,33
m_syncd = (m_syncd) || (m_bfly->o_aux); |
} |
|
void cetick(void) { |
int ce = m_bfly->i_ce, nkce; |
|
tick(); |
|
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
|
if ((ce)&&(nkce > 0)) { |
m_bfly->i_ce = 0; |
for(int kce=0; kce<nkce-1; kce++) |
tick(); |
} |
|
m_bfly->i_ce = ce; |
} |
|
void reset(void) { |
m_bfly->i_ce = 0; |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_coef = 0l; |
m_bfly->i_left = 0; |
m_bfly->i_right = 0; |
tick(); |
m_bfly->i_rst = 0; |
m_bfly->i_reset = 0; |
m_bfly->i_ce = 1; |
// |
// Let's run a RESET test here, forcing the whole butterfly |
93,19 → 150,18
// to be filled with aux=1. If the reset works right, |
// we'll never get an aux=1 output. |
// |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_aux = 1; |
for(int i=0; i<200; i++) { |
m_bfly->i_ce = 1; |
tick(); |
} |
m_bfly->i_ce = 1; |
for(int i=0; i<200; i++) |
cetick(); |
|
// Now here's the RESET line, so let's see what the test does |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_ce = 1; |
m_bfly->i_aux = 1; |
tick(); |
m_bfly->i_rst = 0; |
cetick(); |
m_bfly->i_reset = 0; |
m_syncd = 0; |
|
m_waiting_for_sync_input = true; |
124,41 → 180,42
} |
|
m_bfly->i_ce = 1; |
tick(); |
cetick(); |
|
if ((m_bfly->o_aux)&&(!m_lastaux)) |
printf("\n"); |
printf("n,k=%d,%3d: COEF=%010lx, LFT=%08x, RHT=%08x, A=%d, OLFT =%09lx, ORHT=%09lx, AUX=%d\n", |
printf("n,k=%d,%3d: COEF=%0*lx, LFT=%0*x, RHT=%0*x, A=%d, OLFT =%0*lx, ORHT=%0*lx, AUX=%d\n", |
n,k, |
m_bfly->i_coef & (~(-1l<<40)), |
m_bfly->i_left, |
m_bfly->i_right, |
(2*CWIDTH+3)/4, ubits(m_bfly->i_coef, 2*CWIDTH), |
(2*IWIDTH+3)/4, m_bfly->i_left, |
(2*IWIDTH+3)/4, m_bfly->i_right, |
m_bfly->i_aux, |
m_bfly->o_left, |
m_bfly->o_right, |
(2*OWIDTH+3)/4, (long)m_bfly->o_left, |
(2*OWIDTH+3)/4, (long)m_bfly->o_right, |
m_bfly->o_aux); |
|
if ((m_syncd)&&(m_left[(m_addr-m_offset)&(64-1)] != m_bfly->o_left)) { |
printf("WRONG O_LEFT! (%lx(exp) != %lx(sut))\n", |
printf("WRONG O_LEFT! (%lx(exp) != %lx(sut)\n", |
m_left[(m_addr-m_offset)&(64-1)], |
m_bfly->o_left); |
exit(-1); |
(long)m_bfly->o_left); |
exit(EXIT_FAILURE); |
} |
|
if ((m_syncd)&&(m_right[(m_addr-m_offset)&(64-1)] != m_bfly->o_right)) { |
printf("WRONG O_RIGHT (%10lx(exp) != (%10lx(sut))!\n", |
m_right[(m_addr-m_offset)&(64-1)], m_bfly->o_right); |
exit(-1); |
printf("WRONG O_RIGHT! (%lx(exp) != %lx(sut))\n", |
m_right[(m_addr-m_offset)&(64-1)], |
(long)m_bfly->o_right); |
exit(EXIT_FAILURE); |
} |
|
if ((m_syncd)&&(m_aux[(m_addr-m_offset)&(64-1)] != m_bfly->o_aux)) { |
printf("FAILED AUX CHANNEL TEST (i.e. the SYNC)\n"); |
exit(-1); |
exit(EXIT_FAILURE); |
} |
|
if ((m_addr > TST_BUTTERFLY_MPYDELAY+6)&&(!m_syncd)) { |
printf("NO SYNC PULSE!\n"); |
// exit(-1); |
exit(EXIT_FAILURE); |
} |
|
// Now, let's calculate an "expected" result ... |
241,6 → 298,18
} |
}; |
|
long gentestword(int w, int al, int ar) { |
unsigned long lo, hi, r; |
hi = ((unsigned long)(al&0x0c))<<(w-4); |
hi += (al&3)-2ul; |
|
lo = ((unsigned long)(ar&0x0c))<<(w-4); |
lo += (ar&3)-2ul; |
|
r = (ubits(hi, w) << w) | (ubits(lo, w)); |
return r; |
} |
|
int main(int argc, char **argv, char **envp) { |
Verilated::commandArgs(argc, argv); |
BFLY_TB *bfly = new BFLY_TB; |
251,13 → 320,32
|
const int TESTSZ = 256; |
|
bfly->opentrace("butterfly.vcd"); |
|
bfly->reset(); |
|
// #define ZEROTEST |
#define ZEROTEST bfly->test(9,0,0x0000000000l,0x00000000,0x00000000, 0) |
// Test whether or not the aux channel starts clear, like its supposed to |
|
bfly->test(9,0,0x4000000000l,0x000f0000,0x00000000, 1); |
ZEROTEST; |
ZEROTEST; |
bfly->test(9,0,0x4000000000l,0x00000000,0x000f0000, 0); |
ZEROTEST; |
ZEROTEST; |
bfly->test(9,0,0x4000000000l,0x000f0000,0x000f0000, 0); |
ZEROTEST; |
ZEROTEST; |
bfly->test(9,1,0x4000000000l,0x000f0000,0xfff10000, 0); |
ZEROTEST; |
ZEROTEST; |
bfly->test(9,2,0x4000000000l,0x0000000f,0x0000fff1, 0); |
ZEROTEST; |
ZEROTEST; |
bfly->test(9,3,0x4000000000l,0x0000000f,0x0000000f, 0); |
ZEROTEST; |
ZEROTEST; |
|
bfly->test(9,0,0x4000000000l,0x7fff0000,0x7fff0000, 1); |
bfly->test(9,1,0x4000000000l,0x7fff0000,0x80010000, 0); |
337,6 → 425,30
bfly->test(n,k, cof, lft, rht, aux); |
} |
|
int k = TESTSZ; |
// Exhaustively test |
#if (4*IWIDTH+2*CWIDTH <= 24) |
for(int a=0; a<(1<<(2*IWIDTH)); a++) |
for(int b=0; b<(1<<(2*IWIDTH)); b++) |
for(int c=0; c<(1<<(2*CWIDTH)); c++) |
bfly->test(0, k++, c, a, b, 0); |
|
printf("Exhaust complete\n"); |
#else |
for(int al=0; al<16; al++) |
for(int ar=0; ar<16; ar++) |
for(int bl=0; bl<16; bl++) |
for(int br=0; br<16; br++) |
for(int cl=0; cl<16; cl++) |
for(int cr=0; cr<16; cr++) { |
long a = gentestword(IWIDTH, al, ar); |
long b = gentestword(IWIDTH, bl, br); |
long c = gentestword(CWIDTH, cl, cr); |
bfly->test(0, k++, c, a, b, 0); |
} |
printf("Partial exhaust complete\n"); |
#endif |
|
delete bfly; |
|
printf("SUCCESS!\n"); |
/fft_tb.cpp
5,12 → 5,11
// |
// Purpose: A test-bench for the main program, fftmain.v, of the double |
// clocked FFT. This file may be run autonomously (when |
// fully functional). If so, the last line output will either |
// read "SUCCESS" on success, or some other failure message |
// otherwise. |
// fully functional). If so, the last line output will either read |
// "SUCCESS" on success, or some other failure message otherwise. |
// |
// This file depends upon verilator to both compile, run, and |
// therefore test fftmain.v |
// This file depends upon verilator to both compile, run, and therefore |
// test fftmain.v |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
17,7 → 16,7
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015,2018, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
40,10 → 39,12
// |
/////////////////////////////////////////////////////////////////////////// |
#include <stdio.h> |
#include <stdlib.h> |
#include <math.h> |
#include <fftw3.h> |
|
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "Vfftmain.h" |
#include "twoc.h" |
|
56,8 → 57,11
#define VVAR(A) v__DOT_ ## A |
#endif |
|
|
#ifdef DBLCLKFFT |
#define revstage_iaddr VVAR(_revstage__DOT__iaddr) |
#else |
#define revstage_iaddr VVAR(_revstage__DOT__wraddr) |
#endif |
#define br_sync VVAR(_br_sync) |
#define br_started VVAR(_r_br_started) |
#define w_s2048 VVAR(_w_s2048) |
119,9 → 123,11
double *m_fft_buf; |
bool m_syncd; |
unsigned long m_tickcount; |
VerilatedVcdC* m_trace; |
|
FFT_TB(void) { |
m_fft = new Vfftmain; |
Verilated::traceEverOn(true); |
m_iaddr = m_oaddr = 0; |
m_dumpfp = NULL; |
|
131,39 → 137,75
FFTW_FORWARD, FFTW_MEASURE); |
m_syncd = false; |
m_ntest = 0; |
} |
|
m_tickcount = 0l; |
~FFT_TB(void) { |
closetrace(); |
delete m_fft; |
m_fft = NULL; |
} |
|
virtual void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_fft->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
virtual void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
if ((!m_fft->i_ce)||(m_fft->i_rst)) |
m_tickcount++; |
if (m_fft->i_reset) |
printf("TICK(%s,%s)\n", |
(m_fft->i_rst)?"RST":" ", |
(m_fft->i_reset)?"RST":" ", |
(m_fft->i_ce)?"CE":" "); |
|
m_fft->i_clk = 0; |
m_fft->eval(); |
if (m_trace) |
m_trace->dump((vluint64_t)(10*m_tickcount-2)); |
m_fft->i_clk = 1; |
m_fft->eval(); |
if (m_trace) |
m_trace->dump((vluint64_t)(10*m_tickcount)); |
m_fft->i_clk = 0; |
m_fft->eval(); |
if (m_trace) { |
m_trace->dump((vluint64_t)(10*m_tickcount+5)); |
m_trace->flush(); |
} |
} |
|
m_tickcount++; |
void cetick(void) { |
int ce = m_fft->i_ce, nkce; |
tick(); |
|
/* |
int nrpt = (rand()&0x01f) + 1; |
m_fft->i_ce = 0; |
for(int i=0; i<nrpt; i++) { |
m_fft->i_clk = 0; |
m_fft->eval(); |
m_fft->i_clk = 1; |
m_fft->eval(); |
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
if ((ce)&&(nkce>0)) { |
m_fft->i_ce = 0; |
for(int kce=1; kce < nkce; kce++) |
tick(); |
} |
*/ |
|
m_fft->i_ce = ce; |
} |
|
void reset(void) { |
m_fft->i_ce = 0; |
m_fft->i_rst = 1; |
m_fft->i_reset = 1; |
tick(); |
m_fft->i_rst = 0; |
m_fft->i_reset = 0; |
tick(); |
|
m_iaddr = m_oaddr = m_logbase = 0; |
254,18 → 296,19
printf("%3d : SCALE = %12.6f, WT = %18.1f, ISQ = %15.1f, ", |
m_ntest, scale, wt, isq); |
printf("OSQ = %18.1f, ", osq); |
printf("XISQ = %18.1f\n", xisq); |
printf("XISQ = %18.1f, sqrt = %9.2f\n", xisq, sqrt(xisq)); |
if (xisq > 1.4 * FFTLEN/2) { |
printf("TEST FAIL!! Result is out of bounds from "); |
printf("expected result with FFTW3.\n"); |
// exit(-2); |
// exit(EXIT_FAILURE); |
} |
m_ntest++; |
} |
|
#ifdef DBLCLKFFT |
bool test(ITYP lft, ITYP rht) { |
m_fft->i_ce = 1; |
m_fft->i_rst = 0; |
m_fft->i_reset = 0; |
m_fft->i_left = lft; |
m_fft->i_right = rht; |
|
272,7 → 315,7
m_log[(m_iaddr++)&(NFTLOG*FFTLEN-1)] = lft; |
m_log[(m_iaddr++)&(NFTLOG*FFTLEN-1)] = rht; |
|
tick(); |
cetick(); |
|
if (m_fft->o_sync) { |
if (!m_syncd) { |
339,7 → 382,81
|
return (m_fft->o_sync); |
} |
#else |
bool test(ITYP data) { |
m_fft->i_ce = 1; |
m_fft->i_reset = 0; |
m_fft->i_sample = data; |
|
m_log[(m_iaddr++)&(NFTLOG*FFTLEN-1)] = data; |
|
cetick(); |
|
if (m_fft->o_sync) { |
if (!m_syncd) { |
m_syncd = true; |
printf("ORIGINAL SYNC AT 0x%lx, m_oaddr set to 0x%x\n", m_tickcount, m_oaddr); |
m_logbase = m_iaddr; |
} else printf("RESYNC AT %lx\n", m_tickcount); |
m_oaddr &= (-1<<LGWIDTH); |
} else m_oaddr += 1; |
|
printf("%8x,%5d: %08x -> %011lx\t", |
m_iaddr, m_oaddr, data, m_fft->o_result); |
|
#ifndef APPLY_BITREVERSE_LOCALLY |
printf(" [%3x]%s", m_fft->revstage_iaddr, |
(m_fft->br_sync)?"S" |
:((m_fft->br_started)?".":"x")); |
#endif |
|
printf(" "); |
#if (FFT_SIZE>=2048) |
printf("%s", (m_fft->w_s2048)?"S":"-"); |
#endif |
#if (FFT_SIZE>1024) |
printf("%s", (m_fft->w_s1024)?"S":"-"); |
#endif |
#if (FFT_SIZE>512) |
printf("%s", (m_fft->w_s512)?"S":"-"); |
#endif |
#if (FFT_SIZE>256) |
printf("%s", (m_fft->w_s256)?"S":"-"); |
#endif |
#if (FFT_SIZE>128) |
printf("%s", (m_fft->w_s128)?"S":"-"); |
#endif |
#if (FFT_SIZE>64) |
printf("%s", (m_fft->w_s64)?"S":"-"); |
#endif |
#if (FFT_SIZE>32) |
printf("%s", (m_fft->w_s32)?"S":"-"); |
#endif |
#if (FFT_SIZE>16) |
printf("%s", (m_fft->w_s16)?"S":"-"); |
#endif |
#if (FFT_SIZE>8) |
printf("%s", (m_fft->w_s8)?"S":"-"); |
#endif |
#if (FFT_SIZE>4) |
printf("%s", (m_fft->w_s4)?"S":"-"); |
#endif |
|
printf(" %s%s\n", |
(m_fft->o_sync)?"\t(SYNC!)":"", |
(m_fft->o_result)?" (NZ)":""); |
|
m_data[(m_oaddr )&(FFTLEN-1)] = m_fft->o_result; |
|
if ((m_syncd)&&((m_oaddr&(FFTLEN-1)) == FFTLEN-1)) { |
dumpwrite(); |
checkresults(); |
} |
|
return (m_fft->o_sync); |
} |
#endif |
|
bool test(double lft_r, double lft_i, double rht_r, double rht_i) { |
ITYP ilft, irht, ilft_r, ilft_i, irht_r, irht_i; |
|
351,7 → 468,12
ilft = (ilft_r << IWIDTH) | ilft_i; |
irht = (irht_r << IWIDTH) | irht_i; |
|
#ifdef DBLCLKFFT |
return test(ilft, irht); |
#else |
test(ilft); |
return test(irht); |
#endif |
} |
|
double rdata(int addr) { |
405,6 → 527,7
exit(-1); |
} |
|
fft->opentrace("fft.vcd"); |
fft->reset(); |
|
{ |
414,7 → 537,8
fft->dump(fpout); |
|
// 1. |
fft->test(0.0, 0.0, 32767.0, 0.0); |
double maxv = ((1l<<(IWIDTH-1))-1l); |
fft->test(0.0, 0.0, maxv, 0.0); |
for(int k=0; k<FFTLEN/2-1; k++) |
fft->test(0.0,0.0,0.0,0.0); |
|
422,27 → 546,27
for(int k=0; k<FFTLEN/2; k++) { |
double cl, cr, sl, sr, W; |
W = - 2.0 * M_PI / FFTLEN * (1); |
cl = cos(W * (2*k )) * 16383.0; |
sl = sin(W * (2*k )) * 16383.0; |
cr = cos(W * (2*k+1)) * 16383.0; |
sr = sin(W * (2*k+1)) * 16383.0; |
cl = cos(W * (2*k )) * (double)((1l<<(IWIDTH-2))-1l); |
sl = sin(W * (2*k )) * (double)((1l<<(IWIDTH-2))-1l); |
cr = cos(W * (2*k+1)) * (double)((1l<<(IWIDTH-2))-1l); |
sr = sin(W * (2*k+1)) * (double)((1l<<(IWIDTH-2))-1l); |
fft->test(cl, sl, cr, sr); |
} |
|
// 2. |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=0; k<FFTLEN/2-1; k++) |
fft->test(0.0,0.0,0.0,0.0); |
|
// 3. |
fft->test(0.0,0.0,0.0,0.0); |
fft->test(32767.0, 0.0, 0.0, 0.0); |
fft->test(maxv, 0.0, 0.0, 0.0); |
for(int k=0; k<FFTLEN/2-1; k++) |
fft->test(0.0,0.0,0.0,0.0); |
|
// 4. |
for(int k=0; k<8; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=8; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
|
449,7 → 573,7
// 5. |
if (FFTLEN/2 >= 16) { |
for(int k=0; k<16; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=16; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
457,7 → 581,7
// 6. |
if (FFTLEN/2 >= 32) { |
for(int k=0; k<32; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=32; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
465,7 → 589,7
// 7. |
if (FFTLEN/2 >= 64) { |
for(int k=0; k<64; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=64; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
472,7 → 596,7
|
if (FFTLEN/2 >= 128) { |
for(int k=0; k<128; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=128; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
479,7 → 603,7
|
if (FFTLEN/2 >= 256) { |
for(int k=0; k<256; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=256; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
486,7 → 610,7
|
if (FFTLEN/2 >= 512) { |
for(int k=0; k<256+128; k++) |
fft->test(32767.0, 0.0, 32767.0, 0.0); |
fft->test(maxv, 0.0, maxv, 0.0); |
for(int k=256+128; k<FFTLEN/2; k++) |
fft->test(0.0,0.0,0.0,0.0); |
} |
603,22 → 727,22
|
// 65. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(32767.0,0.0,-32767.0,0.0); |
fft->test(maxv,0.0,-maxv,0.0); |
// 66. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(0.0,-32767.0,0.0,32767.0); |
fft->test(0.0,-maxv,0.0,maxv); |
// 67. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(-32768.0,-32768.0,-32768.0,-32768.0); |
fft->test(-maxv,-maxv,-maxv,-maxv); |
// 68. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(0.0,-32767.0,0.0,32767.0); |
fft->test(0.0,-maxv,0.0,maxv); |
// 69. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(0.0,32767.0,0.0,-32767.0); |
fft->test(0.0,maxv,0.0,-maxv); |
// 70. |
for(int k=0; k<FFTLEN/2; k++) |
fft->test(-32768.0,-32768.0,-32768.0,-32768.0); |
fft->test(-maxv,-maxv,-maxv,-maxv); |
|
// 71. Now let's go for an impulse (SUCCESS) |
fft->test(16384.0, 0.0, 0.0, 0.0); |
722,8 → 846,16
|
fclose(fpout); |
|
if (!fft->m_syncd) { |
printf("FAIL -- NO SYNC\n"); |
goto test_failure; |
} |
|
printf("SUCCESS!!\n"); |
exit(0); |
test_failure: |
printf("TEST FAILED!!\n"); |
exit(0); |
} |
|
|
/fftstage_tb.cpp
0,0 → 1,369
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fftstage_tb.cpp |
// |
// Project: A General Purpose Pipelined FFT Implementation |
// |
// Purpose: A test-bench for a generic FFT stage which has been |
// instantiated by fftgen. Without loss of (much) generality, |
// we'll examine the 2048 fftstage.v. This file may be run autonomously. |
// If so, the last line output will either read "SUCCESS" on success, or |
// some other failure message otherwise. Likewise the exit code will |
// also indicate success (exit(0)) or failure (anything else). |
// |
// This file depends upon verilator to both compile, run, and therefore |
// test fftstage.v. Also, you'll need to place a copy of the cmem_*2048 |
// hex file into the directory where you run this test bench. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2018, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "Vfftstage.h" |
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "twoc.h" |
#include "fftsize.h" |
|
|
#ifdef NEW_VERILATOR |
#define VVAR(A) fftstage__DOT_ ## A |
#else |
#define VVAR(A) v__DOT_ ## A |
#endif |
|
#define cmem VVAR(_cmem) |
#define iaddr VVAR(_iaddr) |
|
#define FFTBITS (FFT_LGWIDTH) |
#define FFTLEN (1<<FFTBITS) |
#define FFTSIZE FFTLEN |
#define FFTMASK (FFTLEN-1) |
#define IWIDTH FFT_IWIDTH |
#define CWIDTH 20 |
#define OWIDTH (FFT_IWIDTH+1) |
#define BFLYSHIFT 0 |
#define LGWIDTH (FFT_LGWIDTH) |
#ifdef DBLCLKFFT |
#define LGSPAN (LGWIDTH-2) |
#else |
#define LGSPAN (LGWIDTH-1) |
#endif |
#define ROUND true |
|
#define SPANLEN (1<<LGSPAN) |
#define SPANMASK (SPANLEN-1) |
#define DBLSPANLEN (1<<(LGSPAN+4)) |
#define DBLSPANMASK (DBLSPANLEN-1) |
|
class FFTSTAGE_TB { |
public: |
Vfftstage *m_ftstage; |
VerilatedVcdC *m_trace; |
long m_oaddr, m_iaddr; |
long m_vals[SPANLEN], m_out[DBLSPANLEN]; |
bool m_syncd; |
int m_offset; |
uint64_t m_tickcount; |
|
FFTSTAGE_TB(void) { |
Verilated::traceEverOn(true); |
m_ftstage = new Vfftstage; |
m_syncd = false; |
m_iaddr = m_oaddr = 0; |
m_offset = 0; |
m_tickcount = 0; |
} |
|
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_ftstage->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_ftstage->i_clk = 0; |
m_ftstage->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount-2)); |
m_ftstage->i_clk = 1; |
m_ftstage->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount)); |
m_ftstage->i_clk = 0; |
m_ftstage->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul*m_tickcount+5)); |
m_trace->flush(); |
} |
} |
|
void cetick(void) { |
int ce = m_ftstage->i_ce, nkce; |
|
tick(); |
nkce = 0; // (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
if ((ce)&&(nkce > 0)) { |
m_ftstage->i_ce = 0; |
for(int kce = 1; kce < nkce; kce++) |
tick(); |
} |
|
m_ftstage->i_ce = ce; |
} |
|
void reset(void) { |
m_ftstage->i_ce = 0; |
m_ftstage->i_reset = 1; |
tick(); |
|
// Let's give it several ticks with no sync |
m_ftstage->i_ce = 0; |
m_ftstage->i_reset = 0; |
for(int i=0; i<8192; i++) { |
m_ftstage->i_data = rand(); |
m_ftstage->i_sync = 0; |
m_ftstage->i_ce = 1; |
|
cetick(); |
|
assert(m_ftstage->o_sync == 0); |
} |
|
m_iaddr = 0; |
m_oaddr = 0; |
m_offset = 0; |
m_syncd = false; |
} |
|
void butterfly(const long cv, const long lft, const long rht, |
long &o_lft, long &o_rht) { |
long cv_r, cv_i; |
long lft_r, lft_i, rht_r, rht_i; |
long o_lft_r, o_lft_i, o_rht_r, o_rht_i; |
|
cv_r = sbits(cv>>CWIDTH, CWIDTH); |
cv_i = sbits(cv, CWIDTH); |
|
lft_r = sbits(lft>>IWIDTH, IWIDTH); |
lft_i = sbits(lft, IWIDTH); |
|
rht_r = sbits(rht>>IWIDTH, IWIDTH); |
rht_i = sbits(rht, IWIDTH); |
|
o_lft_r = lft_r + rht_r; |
o_lft_i = lft_i + rht_i; |
|
o_lft_r &= (~(-1l << OWIDTH)); |
o_lft_i &= (~(-1l << OWIDTH)); |
|
// o_lft_r >>= 1; |
// o_lft_i >>= 1; |
o_lft = (o_lft_r << OWIDTH) | (o_lft_i); |
|
o_rht_r = (cv_r * (lft_r-rht_r)) - (cv_i * (lft_i-rht_i)); |
o_rht_i = (cv_r * (lft_i-rht_i)) + (cv_i * (lft_r-rht_r)); |
|
if (ROUND) { |
if (o_rht_r & (1<<(CWIDTH-3))) |
o_rht_r += (1<<(CWIDTH-3))-1; |
if (o_rht_i & (1<<(CWIDTH-3))) |
o_rht_i += (1<<(CWIDTH-3))-1; |
} |
|
o_rht_r >>= (CWIDTH-2); |
o_rht_i >>= (CWIDTH-2); |
|
o_rht_r &= (~(-1l << OWIDTH)); |
o_rht_i &= (~(-1l << OWIDTH)); |
o_rht = (o_rht_r << OWIDTH) | (o_rht_i); |
|
/* |
printf("%10lx %10lx %10lx -> %10lx %10lx\n", |
cv & ((1l<<(2*CWIDTH))-1l), |
lft & ((1l<<(2*IWIDTH))-1l), |
rht & ((1l<<(2*IWIDTH))-1l), |
o_lft & ((1l<<(2*OWIDTH))-1l), |
o_rht & ((1l<<(2*OWIDTH))-1l)); |
*/ |
} |
|
void test(bool i_sync, long i_data) { |
long cv; |
bool bc; |
int raddr; |
bool failed = false; |
|
m_ftstage->i_reset = 0; |
m_ftstage->i_ce = 1; |
m_ftstage->i_sync = i_sync; |
i_data &= (~(-1l<<(2*IWIDTH))); |
m_ftstage->i_data = i_data; |
|
cv = m_ftstage->cmem[m_iaddr & SPANMASK]; |
bc = m_iaddr & (1<<LGSPAN); |
if (!bc) |
m_vals[m_iaddr & (SPANMASK)] = i_data; |
else { |
int waddr = m_iaddr ^ (1<<LGSPAN); |
waddr &= (DBLSPANMASK); |
if (m_iaddr & (1<<(LGSPAN+1))) |
waddr |= (1<<(LGSPAN)); |
butterfly(cv, m_vals[m_iaddr & (SPANMASK)], i_data, |
m_out[(m_iaddr-SPANLEN) & (DBLSPANMASK)], |
m_out[m_iaddr & (DBLSPANMASK)]); |
/* |
printf("BFLY: C=%16lx M=%8lx I=%10lx -> %10lx %10lx\n", |
cv, m_vals[m_iaddr & (SPANMASK)], i_data, |
m_out[(m_iaddr-SPANLEN)&(DBLSPANMASK)], |
m_out[m_iaddr & (DBLSPANMASK)]); |
*/ |
} |
|
cetick(); |
|
if ((!m_syncd)&&(m_ftstage->o_sync)) { |
m_syncd = true; |
// m_oaddr = m_iaddr - 0x219; |
// m_oaddr = m_iaddr - 0; |
m_offset = m_iaddr; |
m_oaddr = 0; |
|
printf("SYNC!!!!\n"); |
} |
|
raddr = (m_iaddr-m_offset) & DBLSPANMASK; |
/* |
if (m_oaddr & (1<<(LGSPAN+1))) |
raddr |= (1<<LGSPAN); |
*/ |
|
printf("%4ld, %4ld: %d %9lx -> %9lx %d ... %4x %15lx (%10lx)\n", |
(long)m_iaddr, (long)m_oaddr, |
i_sync, (long)(i_data) & (~(-1l << (2*IWIDTH))), |
(long)m_ftstage->o_data, |
m_ftstage->o_sync, |
|
m_ftstage->iaddr&(FFTMASK>>1), |
(long)(m_ftstage->cmem[m_ftstage->iaddr&(SPANMASK>>1)]) & (~(-1l<<(2*CWIDTH))), |
(long)m_out[raddr]); |
|
if ((m_syncd)&&(m_ftstage->o_sync != ((((m_iaddr-m_offset)&((1<<(LGSPAN+1))-1))==0)?1:0))) { |
fprintf(stderr, "Bad output sync (m_iaddr = %lx, m_offset = %x)\n", |
(m_iaddr-m_offset) & SPANMASK, m_offset); |
failed = true; |
} |
|
if (m_syncd) { |
if (m_out[raddr] != m_ftstage->o_data) { |
printf("Bad output data, ([%lx - %x = %x] %lx(exp) != %lx(sut))\n", |
m_iaddr, m_offset, raddr, |
m_out[raddr], (long)m_ftstage->o_data); |
failed = true; |
} |
} else if (m_iaddr > 4096) { |
printf("NO OUTPUT SYNC!\n"); |
failed = true; |
} |
m_iaddr++; |
m_oaddr++; |
|
if (failed) |
exit(-1); |
} |
}; |
|
|
|
int main(int argc, char **argv, char **envp) { |
Verilated::commandArgs(argc, argv); |
FFTSTAGE_TB *ftstage = new FFTSTAGE_TB; |
|
printf("Expecting : IWIDTH = %d, CWIDTH = %d, OWIDTH = %d\n", |
IWIDTH, CWIDTH, OWIDTH); |
|
ftstage->opentrace("fftstage.vcd"); |
ftstage->reset(); |
|
// Medium real (constant) value ... just for starters |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x00200000l); |
// Medium imaginary (constant) value ... just for starters |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x00000020l); |
// Medium sine wave, real |
for(int k=1; k<FFTSIZE; k+=2) { |
long vl; |
vl= (long)(cos(2.0 * M_PI * 1.0 / FFTSIZE * k)*(1l<<30) + 0.5); |
vl &= (-1l << 16); // Turn off the imaginary bit portion |
vl &= (~(-1l << (IWIDTH*2))); // Turn off unused high order bits |
ftstage->test((k==1), vl); |
} |
// Smallest real value |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x00080000l); |
// Smallest imaginary value |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x00000001l); |
// Largest real value |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x200000000l); |
// Largest negative imaginary value |
for(int k=1; k<FFTSIZE; k+=2) |
ftstage->test((k==1), 0x000010000l); |
// Let's try an impulse |
for(int k=0; k<FFTSIZE; k+=2) |
ftstage->test((k==0), (k==0)?0x020000000l:0l); |
// Now, let's clear out the result |
for(int k=0; k<FFTSIZE; k+=2) |
ftstage->test((k==0), 0x000000000l); |
for(int k=0; k<FFTSIZE; k+=2) |
ftstage->test((k==0), 0x000000000l); |
for(int k=0; k<FFTSIZE; k+=2) |
ftstage->test((k==0), 0x000000000l); |
for(int k=0; k<FFTSIZE; k+=2) |
ftstage->test((k==0), 0x000000000l); |
|
printf("SUCCESS! (Offset = %d)\n", ftstage->m_offset); |
delete ftstage; |
|
exit(0); |
} |
/hwbfly_tb.cpp
1,16 → 1,16
//////////////////////////////////////////////////////////////////////////// |
// |
// Filename: butterfly_tb.cpp |
// Filename: hwbfly_tb.cpp |
// |
// Project: A Doubletime Pipelined FFT |
// |
// Purpose: A test-bench for the butterfly.v subfile of the double |
// clocked FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, |
// or some other failure message otherwise. |
// Purpose: A test-bench for the hardware butterfly subfile of the generic |
// pipelined FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, or some |
// other failure message otherwise. |
// |
// This file depends upon verilator to both compile, run, and |
// therefore test butterfly.v |
// This file depends upon verilator to both compile, run, and therefore |
// test hwbfly.v |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
17,7 → 17,7
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015,2018 Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
42,9 → 42,11
#include <stdio.h> |
#include <stdint.h> |
|
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "Vhwbfly.h" |
#include "verilated.h" |
#include "twoc.h" |
#include "fftsize.h" |
|
#ifdef NEW_VERILATOR |
#define VVAR(A) hwbfly__DOT_ ## A |
52,27 → 54,65
#define VVAR(A) v__DOT_ ## A |
#endif |
|
#define IWIDTH TST_BUTTERFLY_IWIDTH |
#define CWIDTH TST_BUTTERFLY_CWIDTH |
#define OWIDTH TST_BUTTERFLY_OWIDTH |
|
class BFLY_TB { |
class HWBFLY_TB { |
public: |
Vhwbfly *m_bfly; |
VerilatedVcdC *m_trace; |
unsigned long m_left[64], m_right[64]; |
bool m_aux[64]; |
int m_addr, m_lastaux, m_offset; |
bool m_syncd; |
uint64_t m_tickcount; |
|
BFLY_TB(void) { |
HWBFLY_TB(void) { |
Verilated::traceEverOn(true); |
m_trace = NULL; |
m_bfly = new Vhwbfly; |
m_addr = 0; |
m_syncd = 0; |
m_tickcount = 0; |
m_bfly->i_reset = 1; |
m_bfly->i_clk = 0; |
m_bfly->eval(); |
m_bfly->i_reset = 0; |
} |
|
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_bfly->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_lastaux = m_bfly->o_aux; |
m_bfly->i_clk = 0; |
m_bfly->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount-2)); |
m_bfly->i_clk = 1; |
m_bfly->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount)); |
m_bfly->i_clk = 0; |
m_bfly->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul*m_tickcount+5)); |
m_trace->flush(); |
} |
|
if ((!m_syncd)&&(m_bfly->o_aux)) |
m_offset = m_addr; |
79,14 → 119,33
m_syncd = (m_syncd) || (m_bfly->o_aux); |
} |
|
void cetick(void) { |
int ce = m_bfly->i_ce, nkce; |
|
tick(); |
|
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
|
if ((ce)&&(nkce > 0)) { |
m_bfly->i_ce = 0; |
for(int kce=0; kce<nkce-1; kce++) |
tick(); |
} |
|
m_bfly->i_ce = ce; |
} |
|
void reset(void) { |
m_bfly->i_ce = 0; |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_coef = 0l; |
m_bfly->i_left = 0; |
m_bfly->i_right = 0; |
tick(); |
m_bfly->i_rst = 0; |
m_bfly->i_reset = 0; |
m_bfly->i_ce = 1; |
// |
// Let's run a RESET test here, forcing the whole butterfly |
93,18 → 152,18
// to be filled with aux=1. If the reset works right, |
// we'll never get an aux=1 output. |
// |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_aux = 1; |
m_bfly->i_ce = 1; |
m_bfly->i_aux = 1; |
for(int i=0; i<200; i++) |
tick(); |
cetick(); |
|
// Now here's the RESET line, so let's see what the test does |
m_bfly->i_rst = 1; |
m_bfly->i_reset = 1; |
m_bfly->i_ce = 1; |
m_bfly->i_aux = 1; |
tick(); |
m_bfly->i_rst = 0; |
cetick(); |
m_bfly->i_reset = 0; |
m_syncd = 0; |
} |
|
111,13 → 170,13
void test(const int n, const int k, const unsigned long cof, |
const unsigned lft, const unsigned rht, const int aux) { |
|
m_bfly->i_coef = cof & (~(-1l << 40)); |
m_bfly->i_left = lft; |
m_bfly->i_right = rht; |
m_bfly->i_coef = ubits(cof, 2*TST_BUTTERFLY_CWIDTH); |
m_bfly->i_left = ubits(lft, 2*TST_BUTTERFLY_IWIDTH); |
m_bfly->i_right = ubits(rht, 2*TST_BUTTERFLY_IWIDTH); |
m_bfly->i_aux = aux & 1; |
|
m_bfly->i_ce = 1; |
tick(); |
cetick(); |
|
if ((m_bfly->o_aux)&&(!m_lastaux)) |
printf("\n"); |
130,30 → 189,49
m_bfly->o_left, |
m_bfly->o_right, |
m_bfly->o_aux); |
#if (FFT_CKPCE == 1) |
printf(", p1 = 0x%08lx p2 = 0x%08lx, p3 = 0x%08lx", |
#define rp_one VVAR(_CKPCE_ONE__DOT__rp_one) |
#define rp_two VVAR(_CKPCE_ONE__DOT__rp_two) |
#define rp_three VVAR(_CKPCE_ONE__DOT__rp_three) |
m_bfly->rp_one, |
m_bfly->rp_two, |
m_bfly->rp_three); |
#elif (FFT_CKPCE == 2) |
#define rp_one VVAR(_genblk1__DOT__CKPCE_TWO__DOT__rp2_one) |
#define rp_two VVAR(_genblk1__DOT__CKPCE_TWO__DOT__rp_two) |
#define rp_three VVAR(_genblk1__DOT__CKPCE_TWO__DOT__rp_three) |
printf(", p1 = 0x%08lx p2 = 0x%08lx, p3 = 0x%08lx", |
m_bfly->rp_one, |
m_bfly->rp_two, |
m_bfly->rp_three); |
#else |
printf("CKPCE = %d\n", FFT_CKPCE); |
#endif |
|
printf("\n"); |
|
if ((m_syncd)&&(m_left[(m_addr-m_offset)&(64-1)] != m_bfly->o_left)) { |
fprintf(stderr, "WRONG O_LEFT! (%lx(exp) != %lx(sut)\n", |
printf("WRONG O_LEFT! (%lx(exp) != %lx(sut)\n", |
m_left[(m_addr-m_offset)&(64-1)], |
m_bfly->o_left); |
exit(-1); |
exit(EXIT_FAILURE); |
} |
|
if ((m_syncd)&&(m_right[(m_addr-m_offset)&(64-1)] != m_bfly->o_right)) { |
fprintf(stderr, "WRONG O_RIGHT! (%lx(exp) != %lx(sut))\n", |
m_right[(m_addr-m_offset)&(64-1)], |
m_bfly->o_right); |
exit(-1); |
printf("WRONG O_RIGHT! (%lx(exp) != %lx(sut))\n", |
m_right[(m_addr-m_offset)&(64-1)], m_bfly->o_right); |
exit(EXIT_FAILURE); |
} |
|
if ((m_syncd)&&(m_aux[(m_addr-m_offset)&(64-1)] != m_bfly->o_aux)) { |
fprintf(stderr, "FAILED AUX CHANNEL TEST (i.e. the SYNC)\n"); |
exit(-1); |
printf("FAILED AUX CHANNEL TEST (i.e. the SYNC)\n"); |
exit(EXIT_FAILURE); |
} |
|
if ((m_addr > 22)&&(!m_syncd)) { |
fprintf(stderr, "NO SYNC PULSE!\n"); |
exit(-1); |
printf("NO SYNC PULSE!\n"); |
exit(EXIT_FAILURE); |
} |
|
// Now, let's calculate an "expected" result ... |
160,20 → 238,20
long rlft, ilft; |
|
// Extract left and right values ... |
rlft = sbits(m_bfly->i_left >> 16, 16); |
ilft = sbits(m_bfly->i_left , 16); |
rlft = sbits(m_bfly->i_left >> IWIDTH, IWIDTH); |
ilft = sbits(m_bfly->i_left , IWIDTH); |
|
// Now repeat for the right hand value ... |
long rrht, irht; |
// Extract left and right values ... |
rrht = sbits(m_bfly->i_right >> 16, 16); |
irht = sbits(m_bfly->i_right , 16); |
rrht = sbits(m_bfly->i_right >> IWIDTH, IWIDTH); |
irht = sbits(m_bfly->i_right , IWIDTH); |
|
// and again for the coefficients |
long rcof, icof; |
// Extract left and right values ... |
rcof = sbits(m_bfly->i_coef >> 20, 20); |
icof = sbits(m_bfly->i_coef , 20); |
rcof = sbits(m_bfly->i_coef >> CWIDTH, CWIDTH); |
icof = sbits(m_bfly->i_coef , CWIDTH); |
|
// Now, let's do the butterfly ourselves ... |
long sumi, sumr, difi, difr; |
198,9 → 276,12
p2 = difi * icof; |
p3 = (difr + difi) * (rcof + icof); |
|
mpyr = p1-p2 + (1<<17); |
mpyi = p3-p1-p2 + (1<<17); |
mpyr = p1-p2; |
mpyi = p3-p1-p2; |
|
mpyr = rndbits(mpyr, (IWIDTH+2)+(CWIDTH+1), OWIDTH+4); |
mpyi = rndbits(mpyi, (IWIDTH+2)+(CWIDTH+1), OWIDTH+4); |
|
/* |
printf("RC=%lx, IC=%lx, ", rcof, icof); |
printf("P1=%lx,P2=%lx,P3=%lx, ", p1,p2,p3); |
211,12 → 292,15
long o_left_r, o_left_i, o_right_r, o_right_i; |
unsigned long o_left, o_right; |
|
o_left_r = sumr & 0x01ffff; o_left_i = sumi & 0x01ffff; |
o_left = (o_left_r << 17) | (o_left_i); |
o_left_r = rndbits(sumr<<(CWIDTH-2), CWIDTH+IWIDTH+3, OWIDTH+4); |
o_left_r = ubits(o_left_r, OWIDTH); |
o_left_i = rndbits(sumi<<(CWIDTH-2), CWIDTH+IWIDTH+3, OWIDTH+4); |
o_left_i = ubits(o_left_i, OWIDTH); |
o_left = (o_left_r << OWIDTH) | (o_left_i); |
|
o_right_r = (mpyr>>18) & 0x01ffff; |
o_right_i = (mpyi>>18) & 0x01ffff; |
o_right = (o_right_r << 17) | (o_right_i); |
o_right_r = ubits(mpyr, OWIDTH); |
o_right_i = ubits(mpyi, OWIDTH); |
o_right = (o_right_r << OWIDTH) | (o_right_i); |
/* |
printf("oR_r = %lx, ", o_right_r); |
printf("oR_i = %lx\n", o_right_i); |
232,7 → 316,7
|
int main(int argc, char **argv, char **envp) { |
Verilated::commandArgs(argc, argv); |
BFLY_TB *bfly = new BFLY_TB; |
HWBFLY_TB *bfly = new HWBFLY_TB; |
int16_t ir0, ii0, lstr, lsti; |
int32_t sumr, sumi, difr, difi; |
int32_t smr, smi, dfr, dfi; |
240,6 → 324,8
|
const int TESTSZ = 256; |
|
bfly->opentrace("hwbfly.vcd"); |
|
bfly->reset(); |
|
bfly->test(9,0,0x4000000000l,0x7fff0000,0x7fff0000, 1); |
/laststage_tb.cpp
0,0 → 1,332
//////////////////////////////////////////////////////////////////////////// |
// |
// Filename: laststage_tb.cpp |
// |
// Project: A Doubletime Pipelined FFT |
// |
// Purpose: A test-bench for the laststage.v subfile of the general purpose |
// pipelined FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, or some |
// other failure message otherwise. |
// |
// This file depends upon verilator to both compile, run, and therefore |
// test laststage.v |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2018 Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
#include <stdio.h> |
#include <stdint.h> |
|
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "Vlaststage.h" |
#include "twoc.h" |
|
#define IWIDTH 16 |
#define OWIDTH (IWIDTH+1) |
#define SHIFT 0 |
#define ROUND 1 |
|
#define ASIZ 32 |
#define AMSK (ASIZ-1) |
|
class LASTSTAGE_TB { |
public: |
Vlaststage *m_last; |
VerilatedVcdC *m_trace; |
#ifdef DBLCLKFFT |
unsigned long m_left[ASIZ], m_right[ASIZ]; |
#else |
unsigned long m_data[ASIZ]; |
#endif |
bool m_syncd; |
int m_addr, m_offset; |
unsigned long m_tickcount; |
|
LASTSTAGE_TB(void) { |
Verilated::traceEverOn(true); |
m_last = new Vlaststage; |
m_tickcount = 0; |
m_syncd = false; m_addr = 0, m_offset = 0; |
} |
|
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_last->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_last->i_clk = 0; |
m_last->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul * m_tickcount - 2)); |
m_last->i_clk = 1; |
m_last->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul * m_tickcount)); |
m_last->i_clk = 0; |
m_last->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul * m_tickcount + 5)); |
m_trace->flush(); |
} |
m_last->i_reset = 0; |
m_last->i_sync = 0; |
} |
|
void cetick(void) { |
int nkce; |
|
tick(); |
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
if ((m_last->i_ce)&&(nkce > 0)) { |
m_last->i_ce = 0; |
for(int kce = 1; kce < nkce; kce++) |
tick(); |
m_last->i_ce = 1; |
} |
} |
|
void reset(void) { |
m_last->i_reset = 1; |
tick(); |
|
m_syncd = false; m_addr = 0, m_offset = 0; |
} |
|
void check_results(void) { |
bool failed = false; |
|
if ((!m_syncd)&&(m_last->o_sync)) { |
m_syncd = true; |
m_offset = m_addr; |
printf("SYNCD at %d\n", m_addr); |
} |
|
#ifdef DBLCLKFFT |
int ir0, ir1, ii0, ii1, or0, oi0, or1, oi1; |
|
ir0 = sbits(m_left[ (m_addr-m_offset)&AMSK]>>IWIDTH, IWIDTH); |
ir1 = sbits(m_right[(m_addr-m_offset)&AMSK]>>IWIDTH, IWIDTH); |
ii0 = sbits(m_left[ (m_addr-m_offset)&AMSK], IWIDTH); |
ii1 = sbits(m_right[(m_addr-m_offset)&AMSK], IWIDTH); |
|
|
or0 = sbits(m_last->o_left >> OWIDTH, OWIDTH); |
oi0 = sbits(m_last->o_left , OWIDTH); |
or1 = sbits(m_last->o_right >> OWIDTH, OWIDTH); |
oi1 = sbits(m_last->o_right , OWIDTH); |
|
|
// Sign extensions |
printf("k=%3d: IN = %08x:%08x, OUT =%09lx:%09lx, S=%d\n", |
m_addr, m_last->i_left, m_last->i_right, |
m_last->o_left, m_last->o_right, |
m_last->o_sync); |
|
/* |
printf("\tI0 = { %x : %x }, I1 = { %x : %x }, O0 = { %x : %x }, O1 = { %x : %x }\n", |
ir0, ii0, ir1, ii1, or0, oi0, or1, oi1); |
*/ |
|
if (m_syncd) { |
if (or0 != (ir0 + ir1)) { |
printf("FAIL 1: or0 != (ir0+ir1), or %x(exp) != %x(sut)\n", (ir0+ir1), or0); |
failed=true;} |
if (oi0 != (ii0 + ii1)) {printf("FAIL 2\n"); failed=true;} |
if (or1 != (ir0 - ir1)) {printf("FAIL 3\n"); failed=true;} |
if (oi1 != (ii0 - ii1)) {printf("FAIL 4\n"); failed=true;} |
} else if (m_addr > 20) { |
printf("NO SYNC!\n"); |
failed = true; |
} |
#else |
int or0, oi0; |
int sumr, sumi, difr, difi; |
int ir0, ii0, ir1, ii1, ir2, ii2, ir3, ii3, irn, iin; |
|
irn = sbits(m_data[(m_addr-m_offset+2)&AMSK]>>IWIDTH, IWIDTH); |
iin = sbits(m_data[(m_addr-m_offset+2)&AMSK], IWIDTH); |
ir0 = sbits(m_data[(m_addr-m_offset+1)&AMSK]>>IWIDTH, IWIDTH); |
ii0 = sbits(m_data[(m_addr-m_offset+1)&AMSK], IWIDTH); |
ir1 = sbits(m_data[(m_addr-m_offset )&AMSK]>>IWIDTH, IWIDTH); |
ii1 = sbits(m_data[(m_addr-m_offset )&AMSK], IWIDTH); |
ir2 = sbits(m_data[(m_addr-m_offset-1)&AMSK]>>IWIDTH, IWIDTH); |
ii2 = sbits(m_data[(m_addr-m_offset-1)&AMSK], IWIDTH); |
ir3 = sbits(m_data[(m_addr-m_offset-2)&AMSK]>>IWIDTH, IWIDTH); |
ii3 = sbits(m_data[(m_addr-m_offset-2)&AMSK], IWIDTH); |
|
sumr = ir1 + ir0; |
sumi = ii1 + ii0; |
|
difr = ir2 - ir1; |
difi = ii2 - ii1; |
|
or0 = sbits(m_last->o_val >> OWIDTH, OWIDTH); |
oi0 = sbits(m_last->o_val , OWIDTH); |
|
printf("IR0 = %08x, IR1 = %08x, IR2 = %08x, ", |
ir0, ir1, ir2); |
printf("II0 = %08x, II1 = %08x, II2 = %08x, ", |
ii0, ii1, ii2); |
// Sign extensions |
printf("k=%3d: IN = %08x, %c, OUT =%09lx, S=%d\n", |
m_addr, m_last->i_val, |
m_last->i_sync ? 'S':' ', |
m_last->o_val, m_last->o_sync); |
|
|
if ((m_syncd)&&(0 == ((m_addr-m_offset)&1))) { |
if (or0 != sumr) { |
printf("FAIL 1: or0 != (ir0+ir1), or %x(exp) != %x(sut)\n", sumr, or0); |
failed=true; |
} if (oi0 != sumi) { |
printf("FAIL 2\n"); |
failed=true; |
} |
} else if ((m_syncd)&&(1 == ((m_addr-m_offset)&1))) { |
if (or0 != difr) { |
printf("FAIL 3: or0 != (ir1-ir0), or %x(exp) != %x(sut)\n", difr, or0); |
failed=true; |
} if (oi0 != difi) { |
printf("FAIL 4: oi0 != (ii1-ii0), or %x(exp) != %x(sut)\n", difi, oi0); |
failed=true; |
} |
} else if (m_addr > 20) { |
printf("NO SYNC!\n"); |
failed = true; |
} |
#endif |
if (failed) |
exit(-2); |
} |
|
void sync(void) { |
m_last->i_sync = 1; |
} |
|
void test(unsigned long left, unsigned long right) { |
m_last->i_ce = 1; |
if (m_last->i_sync) |
m_addr = 0; |
#ifdef DBLCLKFFT |
m_last->i_left = left; |
m_last->i_right = right; |
|
m_left[ m_addr&AMSK] = m_last->i_left; |
m_right[m_addr&AMSK] = m_last->i_right; |
m_addr++; |
|
cetick(); |
#else |
m_last->i_val = left; |
m_data[ m_addr&AMSK] = m_last->i_val; |
m_addr = (m_addr+1); |
cetick(); |
|
check_results(); |
|
m_last->i_val = right; |
m_data[m_addr&AMSK] = m_last->i_val; |
m_addr = (m_addr+1)&AMSK; |
cetick(); |
#endif |
|
check_results(); |
} |
|
void test(int ir0, int ii0, int ir1, int ii1) { |
unsigned long left, right, mask = (1<<IWIDTH)-1; |
|
left = ((ir0&mask) << IWIDTH) | (ii0 & mask); |
right = ((ir1&mask) << IWIDTH) | (ii1 & mask); |
test(left, right); |
} |
}; |
|
int main(int argc, char **argv, char **envp) { |
Verilated::commandArgs(argc, argv); |
LASTSTAGE_TB *tb = new LASTSTAGE_TB; |
|
tb->opentrace("laststage.vcd"); |
tb->reset(); |
|
tb->sync(); |
|
tb->test( 1, 0,0,0); |
tb->test( 0, 2,0,0); |
tb->test( 0, 0,4,0); |
tb->test( 0, 0,0,8); |
|
tb->test( 0, 0,0,0); |
|
tb->test(16,16,0,0); |
tb->test(0,0,16,16); |
tb->test(16,-16,0,0); |
tb->test(0,0,16,-16); |
tb->test(16,16,0,0); |
tb->test(0,0,16,16); |
|
for(int k=0; k<64; k++) { |
int16_t ir0, ii0, ir1, ii1; |
|
// Let's pick some random values, ... |
ir0 = rand(); if (ir0&4) ir0 = -ir0; |
ii0 = rand(); if (ii0&2) ii0 = -ii0; |
ir1 = rand(); if (ir1&1) ir1 = -ir1; |
ii1 = rand(); if (ii1&8) ii1 = -ii1; |
|
tb->test(ir0, ii0, ir1, ii1); |
|
} |
|
delete tb; |
|
printf("SUCCESS!\n"); |
exit(0); |
} |
|
|
|
|
|
|
/mpy_tb.cpp
2,7 → 2,7
// |
// Filename: mpy_tb.cpp |
// |
// Project: A Doubletime Pipelined FFT |
// Project: A General Purpose Pipelined FFT Implementation |
// |
// Purpose: A test-bench for the shift and add shiftaddmpy.v subfile of |
// the double clocked FFT. This file may be run autonomously. |
17,7 → 17,7
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015-2018, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
39,7 → 39,11
// |
// |
/////////////////////////////////////////////////////////////////////////// |
#include "verilated.h" |
#include "verilated_vcd_c.h" |
|
#include "fftsize.h" |
|
#ifdef USE_OLD_MULTIPLY |
#include "Vshiftaddmpy.h" |
typedef Vshiftaddmpy Vmpy; |
54,17 → 58,20
#define DELAY ((AW/2)+(AW&1)+2) |
#endif |
|
#include "verilated.h" |
#include "twoc.h" |
|
class MPYTB { |
public: |
Vmpy *mpy; |
long vals[32]; |
int m_addr; |
Vmpy *m_mpy; |
VerilatedVcdC *m_trace; |
long vals[32]; |
int m_addr; |
uint64_t m_tickcount; |
|
MPYTB(void) { |
mpy = new Vmpy; |
Verilated::traceEverOn(true); |
m_mpy = new Vmpy; |
m_tickcount = 0; |
|
for(int i=0; i<32; i++) |
vals[i] = 0; |
71,24 → 78,68
m_addr = 0; |
} |
~MPYTB(void) { |
delete mpy; |
closetrace(); |
delete m_mpy; |
} |
|
void tick(void) { |
mpy->i_clk = 0; |
mpy->eval(); |
mpy->i_clk = 1; |
mpy->eval(); |
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_mpy->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_mpy->i_clk = 0; |
m_mpy->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount-2)); |
m_mpy->i_clk = 1; |
m_mpy->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount)); |
m_mpy->i_clk = 0; |
m_mpy->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul*m_tickcount+5)); |
m_trace->flush(); |
} |
} |
|
void cetick(void) { |
int ce = m_mpy->i_ce, nkce; |
|
tick(); |
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
if ((ce)&&(nkce>0)) { |
m_mpy->i_ce = 0; |
for(int kce=1; kce<nkce; kce++) |
tick(); |
} |
|
m_mpy->i_ce = ce; |
} |
|
void reset(void) { |
mpy->i_clk = 0; |
mpy->i_ce = 1; |
mpy->i_a = 0; |
mpy->i_b = 0; |
m_mpy->i_clk = 0; |
m_mpy->i_ce = 1; |
m_mpy->i_a_unsorted = 0; |
m_mpy->i_b_unsorted = 0; |
|
for(int k=0; k<20; k++) |
tick(); |
cetick(); |
} |
|
bool test(const int ia, const int ib) { |
97,23 → 148,21
|
a = sbits(ia, AW); |
b = sbits(ib, BW); |
mpy->i_ce = 1; |
mpy->i_a = ubits(a, AW); |
mpy->i_b = ubits(b, BW); |
m_mpy->i_ce = 1; |
m_mpy->i_a_unsorted = ubits(a, AW); |
m_mpy->i_b_unsorted = ubits(b, BW); |
|
vals[m_addr&31] = a * b; |
|
tick(); |
if (rand()&1) { |
mpy->i_ce = 0; |
tick(); |
} |
cetick(); |
|
printf("k=%3d: A =%04x, B =%05x -> O = %9lx (ANS=%10lx)\n", |
m_addr, (int)ubits(a,AW), (int)ubits(b,BW), |
(long)mpy->o_r, ubits(vals[m_addr&31], AW+BW+4)); |
printf("k=%3d: A =%0*x, B =%0*x -> O = %*lx (ANS=%*lx)\n", |
m_addr, (AW+3)/4, (int)ubits(a,AW), |
(BW+3)/4, (int)ubits(b,BW), |
(AW+BW+3)/4, (long)m_mpy->o_r, |
(AW+BW+7)/4, ubits(vals[m_addr&31], AW+BW+4)); |
|
out = sbits(mpy->o_r, AW+BW); |
out = sbits(m_mpy->o_r, AW+BW); |
|
m_addr++; |
|
131,6 → 180,7
Verilated::commandArgs(argc, argv); |
MPYTB *tb = new MPYTB; |
|
tb->opentrace("mpy.vcd"); |
tb->reset(); |
|
for(int k=0; k<15; k++) { |
149,10 → 199,16
tb->test(a, b); |
} |
|
for(int k=0; k<2048; k++) { |
int a, b, out; |
|
tb->test(rand(), rand()); |
if (AW+BW <= 20) { |
// Exhaustive test |
for(int a=0; a< (1<<AW); a++) |
for(int b=0; b< (1<<BW); b++) |
tb->test(a, b); |
printf("Exhaust complete\n"); |
} else { |
// Pseudorandom test |
for(int k=0; k<2048; k++) |
tb->test(rand(), rand()); |
} |
|
delete tb; |
/qtrstage_tb.cpp
6,11 → 6,11
// |
// Purpose: A test-bench for the qtrstage.v subfile of the double |
// clocked FFT. This file may be run autonomously. If so, |
// the last line output will either read "SUCCESS" on success, |
// or some other failure message otherwise. |
// the last line output will either read "SUCCESS" on success, or some |
// other failure message otherwise. |
// |
// This file depends upon verilator to both compile, run, and |
// therefore test qtrstage.v |
// This file depends upon verilator to both compile, run, and therefore |
// test qtrstage.v |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
17,7 → 17,7
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015,2018, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
42,8 → 42,9
#include <stdio.h> |
#include <stdint.h> |
|
#include "verilated.h" |
#include "verilated_vcd_c.h" |
#include "Vqtrstage.h" |
#include "verilated.h" |
#include "twoc.h" |
#include "fftsize.h" |
|
72,30 → 73,78
class QTRTEST_TB { |
public: |
Vqtrstage *m_qstage; |
unsigned long m_data[ASIZ]; |
VerilatedVcdC *m_trace; |
unsigned long m_data[ASIZ], m_tickcount; |
int m_addr, m_offset; |
bool m_syncd; |
|
QTRTEST_TB(void) { |
Verilated::traceEverOn(true); |
m_trace = NULL; |
m_qstage = new Vqtrstage; |
m_addr = 0; m_offset = 6; m_syncd = false; |
m_addr = 0; |
m_offset = 6; |
m_syncd = false; |
m_tickcount = 0; |
} |
|
void opentrace(const char *vcdname) { |
if (!m_trace) { |
m_trace = new VerilatedVcdC; |
m_qstage->trace(m_trace, 99); |
m_trace->open(vcdname); |
} |
} |
|
void closetrace(void) { |
if (m_trace) { |
m_trace->close(); |
delete m_trace; |
m_trace = NULL; |
} |
} |
|
void tick(void) { |
m_tickcount++; |
|
m_qstage->i_clk = 0; |
m_qstage->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount-2)); |
m_qstage->i_clk = 1; |
m_qstage->eval(); |
if (m_trace) m_trace->dump((uint64_t)(10ul*m_tickcount)); |
m_qstage->i_clk = 0; |
m_qstage->eval(); |
if (m_trace) { |
m_trace->dump((uint64_t)(10ul*m_tickcount+5)); |
m_trace->flush(); |
} |
|
m_qstage->i_sync = 0; |
} |
|
void cetick(void) { |
int nkce; |
|
tick(); |
nkce = (rand()&1); |
#ifdef FFT_CKPCE |
nkce += FFT_CKPCE; |
#endif |
if ((m_qstage->i_ce)&&(nkce>0)) { |
m_qstage->i_ce = 0; |
for(int kce = 1; kce < nkce; kce++) |
tick(); |
m_qstage->i_ce = 1; |
} |
} |
|
void reset(void) { |
m_qstage->i_ce = 0; |
m_qstage->i_rst = 1; |
m_qstage->i_reset = 1; |
tick(); |
m_qstage->i_ce = 0; |
m_qstage->i_rst = 0; |
m_qstage->i_reset = 0; |
tick(); |
|
m_addr = 0; m_offset = 6; m_syncd = false; |
102,18 → 151,22
} |
|
void check_results(void) { |
int ir0, ii0, ir1, ii1, ir2, ii2; |
int sumr, sumi, difr, difi, or0, oi0; |
bool fail = false; |
|
if ((!m_syncd)&&(m_qstage->o_sync)) { |
m_syncd = true; |
assert(m_addr == m_offset); |
m_offset = m_addr; |
printf("VALID-SYNC!!\n"); |
} |
|
if (!m_syncd) |
return; |
|
#ifdef DBLCLKFFT |
int ir0, ii0, ir1, ii1, ir2, ii2; |
|
ir0 = sbits(m_data[(m_addr-m_offset-1)&AMSK]>>IWIDTH, IWIDTH); |
ii0 = sbits(m_data[(m_addr-m_offset-1)&AMSK], IWIDTH); |
ir1 = sbits(m_data[(m_addr-m_offset )&AMSK]>>IWIDTH, IWIDTH); |
140,11 → 193,49
if (oi0 != difi) { |
printf("FAIL 4: oi0 != difi (%x(exp) != %x(sut))\n", difi, oi0); fail = true;} |
} |
#else |
int locn = (m_addr-m_offset)&AMSK; |
int ir1, ii1, ir3, ii3, ir5, ii5; |
|
if (m_qstage->o_sync != ((((m_addr-m_offset)&127) == 0)?1:0)) { |
printf("BAD O-SYNC, m_addr = %d, m_offset = %d\n", m_addr, m_offset); fail = true; |
ir5 = sbits(m_data[(m_addr-m_offset-2)&AMSK]>>IWIDTH, IWIDTH); |
ii5 = sbits(m_data[(m_addr-m_offset-2)&AMSK], IWIDTH); |
ir3 = sbits(m_data[(m_addr-m_offset )&AMSK]>>IWIDTH, IWIDTH); |
ii3 = sbits(m_data[(m_addr-m_offset )&AMSK], IWIDTH); |
ir1 = sbits(m_data[(m_addr-m_offset+2)&AMSK]>>IWIDTH, IWIDTH); |
ii1 = sbits(m_data[(m_addr-m_offset+2)&AMSK], IWIDTH); |
|
sumr = ir3 + ir1; |
sumi = ii3 + ii1; |
difr = ir5 - ir3; |
difi = ii5 - ii3; |
|
or0 = sbits(m_qstage->o_data >> OWIDTH, OWIDTH); |
oi0 = sbits(m_qstage->o_data, OWIDTH); |
|
if (0==((locn)&2)) { |
if (or0 != sumr) { |
printf("FAIL 1: or0 != sumr (%x(exp) != %x(sut))\n", sumr, or0); fail = true; |
} |
if (oi0 != sumi) { |
printf("FAIL 2: oi0 != sumi (%x(exp) != %x(sut))\n", sumi, oi0); fail = true;} |
} else if (2==((m_addr-m_offset)&3)) { |
if (or0 != difr) { |
printf("FAIL 3: or0 != difr (%x(exp) != %x(sut))\n", difr, or0); fail = true;} |
if (oi0 != difi) { |
printf("FAIL 4: oi0 != difi (%x(exp) != %x(sut))\n", difi, oi0); fail = true;} |
} else if (3==((m_addr-m_offset)&3)) { |
if (or0 != difi) { |
printf("FAIL 3: or0 != difr (%x(exp) != %x(sut))\n", difr, or0); fail = true;} |
if (oi0 != -difr) { |
printf("FAIL 4: oi0 != difi (%x(exp) != %x(sut))\n", difi, oi0); fail = true;} |
} |
|
// if (m_qstage->o_sync != ((((m_addr-m_offset)&127) == 0)?1:0)) { |
// printf("BAD O-SYNC, m_addr = %d, m_offset = %d\n", m_addr, m_offset); fail = true; |
// } |
#endif |
|
|
if (fail) |
exit(-1); |
} |
159,6 → 250,7
m_qstage->i_ce = 1; |
m_qstage->i_data = data; |
// m_qstage->i_sync = (((m_addr&127)==2)?1:0); |
// printf("DATA[%08x] = %08x ... ", m_addr, data); |
m_data[ (m_addr++)&AMSK] = data; |
tick(); |
|
172,7 → 264,11
m_qstage->diff_i, |
m_qstage->pipeline, |
m_qstage->iaddr, |
#ifdef DBLCLKFFT |
m_qstage->imem, |
#else |
m_qstage->imem[1], |
#endif |
m_qstage->wait_for_sync); |
|
check_results(); |
202,17 → 298,57
int16_t ir0, ii0, ir1, ii1, ir2, ii2; |
int32_t sumr, sumi, difr, difi; |
|
tb->opentrace("qtrstage.vcd"); |
tb->reset(); |
|
tb->test( 16, 0); |
tb->test( 16, 0); |
tb->sync(); |
|
tb->test( 8, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
|
tb->test( 0, 4); |
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
|
tb->test( 0, 0); |
tb->test( 32, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
|
tb->test( 0, 0); |
tb->test( 0, 64); |
tb->test( 0, 0); |
tb->test( 0, 0); |
|
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test(128, 0); |
tb->test( 0, 0); |
|
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0,256); |
tb->test( 0, 0); |
|
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 2, 0); |
|
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0, 0); |
tb->test( 0, 1); |
|
tb->test( 0, 16); |
tb->test( 0, 16); |
tb->test( 16, 0); |
tb->test(-16, 0); |
tb->test( 0, 16); |
tb->test( 0,-16); |
|
for(int k=0; k<1060; k++) { |
tb->random_test(); |