OpenCores
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();

powered by: WebSVN 2.1.0

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