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

Subversion Repositories dblclockfft

[/] [dblclockfft/] [trunk/] [bench/] [cpp/] [fftstage_tb.cpp] - Rev 36

Go to most recent revision | Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
// 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);
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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