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

Subversion Repositories zipcpu

[/] [zipcpu/] [trunk/] [sim/] [verilator/] [div_tb.cpp] - Rev 208

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

///////////////////////////////////////////////////////////////////////////////
//
// Filename:	div_tb.cpp
//
// Project:	Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose:	Bench testing for the divide unit found within the Zip CPU.
//
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015, 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.
//
// License:	GPL, v3, as defined and found on www.gnu.org,
//		http://www.gnu.org/licenses/gpl.html
//
//
///////////////////////////////////////////////////////////////////////////////
//
//
#include <signal.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
 
#include <ctype.h>
 
#include "verilated.h"
#include "Vdiv.h"
 
#include "testb.h"
// #include "twoc.h"
 
#define	DIVASSERT(A) do { if (!(A)) { closetrace(); } assert(A); } while(0)
 
class	DIV_TB : public TESTB<Vdiv> {
public:
	DIV_TB(void) {
	}
 
	~DIV_TB(void) {}
 
	void	reset(void) {
		// m_flash.debug(false);
		TESTB<Vdiv>::reset();
	}
 
	void	bprint(char *str, int nbits, unsigned long v) {
		while(*str)
			str++;
		for(int i=0; i<nbits; i++) {
			if ((1l<<(nbits-1-i))&v)
				*str++ = '1';
			else
				*str++ = '0';
			if (((nbits-1-i)&3)==0)
				*str++ = ' ';
		} *str = '\0';
	}
 
	void	dbgdump(void) {
		char	outstr[2048], *s;
		sprintf(outstr, "Tick %4ld %s%s%s%s%s%s%s %2d(%s= 0)",
			m_tickcount,
			(m_core->o_busy)?"B":" ",
			(m_core->v__DOT__r_busy)?"R":" ",
			(m_core->o_valid)?"V":" ",
			(m_core->i_wr)?"W":" ",
			(m_core->v__DOT__pre_sign)?"+":" ",
			(m_core->v__DOT__r_sign)?"-":" ",
			(m_core->v__DOT__r_z)?"Z":" ",
			m_core->v__DOT__r_bit,
			(m_core->v__DOT__last_bit)?"=":"!");
		s = &outstr[strlen(outstr)];
		sprintf(s, "%s\n%10s %40s",s, "Div","");
			s = &s[strlen(s)];
		bprint( s, 32, m_core->v__DOT__r_dividend);
			s=&s[strlen(s)];
		sprintf(s, "%s\n%10s ",s, "Div"); s = &s[strlen(s)];
		bprint( s, 64, m_core->v__DOT__r_divisor);
			s=&s[strlen(s)];
		sprintf(s, "%s\n%10s %40s",s, "Q",""); s=&s[strlen(s)];
		bprint( s, 32, m_core->o_quotient); s = &s[strlen(s)];
		sprintf(s, "%s\n%10s %38s",s, "Diff","");
			s=&s[strlen(s)];
		bprint( s, 33, m_core->v__DOT__diff); s = &s[strlen(s)];
		strcat(s, "\n");
		puts(outstr);
	}
 
	void	tick(void) {
		bool	debug = false;
 
		if (debug)
			dbgdump();
		TESTB<Vdiv>::tick();
	}
 
	void	divtest(uint32_t n, uint32_t d, uint32_t ans, bool issigned) {
		const bool	dbg = false;
 
		// The test bench is supposed to assert that we are idle when
		// we come in here.
		DIVASSERT(m_core->o_busy == 0);
 
		// Request a divide
		m_core->i_rst = 0;
		m_core->i_wr = 1;
		m_core->i_signed = (issigned)?1:0;
		m_core->i_numerator = n;
		m_core->i_denominator = d;
 
		// Tick once for the request to be registered
		tick();
 
		// Clear the input lines.
		m_core->i_wr = 0;
		m_core->i_signed = 0;
		m_core->i_numerator = 0;
		m_core->i_denominator = 0;
 
		// Make certain busy is immediately true upon the first clock
		// after we issue the divide, and that our result is not also
		// listed as a valid result.
		if (!m_core->o_busy) {
			closetrace();
			DIVASSERT(m_core->o_busy);
		} if (m_core->o_valid != 0) {
			closetrace();
			DIVASSERT(m_core->o_valid == 0);
		}
 
		// while((!m_core->o_valid)&&(!m_core->o_err))
		while(!m_core->o_valid) {
			// If we aren't yet valid, we'd better at least
			// be busy--the CPU requires this.
			if (!m_core->o_busy) {
				// We aren't valid, and we aren't busy.  This
				// is a test failure.
				dbgdump();
				closetrace();
				DIVASSERT(m_core->o_busy);
			}
 
			// Let the algorithm work for another clock tick.
			tick();
		} if (dbg) dbgdump();
 
		// Insist that the core not be busy any more, now that a valid
		// result has been produced.
		if (m_core->o_busy) {
			closetrace();
			DIVASSERT(!m_core->o_busy);
		}
 
		if (dbg) {
			printf("%s%s: %d / %d =? %d\n",
				(m_core->o_valid)?"V":" ",
				(m_core->o_err)?"E":" ",
				n, d, m_core->o_quotient);
		}
 
 
		// Now that we're done, we need to check the result.
		//
		// First case to check: was there an error condition or, if not,
		// should there have been one?
		if (d == 0) {
			// We attempted to divide by zero, the result should've
			// been an error condition.  Let's check:
			// Then insist on a division by zero error
			if (!m_core->o_err) {
				// Don't forget to close the trace before the
				// assert, lest the file not get the final
				// values into it.
				closetrace();
				DIVASSERT(m_core->o_err);
			}
		} else if (m_core->o_err) {
			// Otherwise, there should not have been any divide
			// errors.  The only errors allowed should be the
			// divide by zero.  So, this is an error.  Let's
			// stop and report it.
			closetrace();
			DIVASSERT(!m_core->o_err);
		} else if (ans != (uint32_t)m_core->o_quotient) {
			// The other problem we might encounter would be if the
			// result doesn't match the one we are expecting.
			//
			// Stop on this bug as well.
			//
			closetrace();
			DIVASSERT(ans == (uint32_t)m_core->o_quotient);
		}
 
		if(((m_core->o_quotient == 0)&&((m_core->o_flags&1)==0))
			||((m_core->o_quotient!= 0)&&((m_core->o_flags&1)!=0))){
			fprintf(stderr, "Z-FLAG DOES NOT MATCH: FLAGS = %d, QUOTIENT = %08x\n", m_core->o_flags, m_core->o_quotient);
			DIVASSERT((m_core->o_quotient!= 0)^(m_core->o_flags&1));
		}
	}
 
	// Test a signed divide
	void	divs(int n, int d) {
		int	ans;
		// Calculate the answer we *should* get from the divide
		ans = (d==0)?0:	(n / d);
 
		divtest((uint32_t)n, (uint32_t)d, (uint32_t)ans, true);
	}
 
	// Test an unsigned divide
	void	divu(unsigned n, unsigned d) {
		unsigned	ans;
 
		// Pre-Calculate the answer we *should* get from the divide
		ans = (d==0)?0:	(n / d);
 
		divtest((uint32_t)n, (uint32_t)d, (uint32_t)ans, false);
	}
 
	// divide() is just another name for a signed divide--just switch to
	// that function call instead.
	void	divide(int n, int d) {
		divs(n,d);
	}
};
 
//
// Standard usage functions.
//
// Notice that the test bench provides no options.  Everything is
// self-contained.
void	usage(void) {
	printf("USAGE: div_tb\n");
	printf("\n");
	printf("\t\n");
}
 
//
int	main(int argc, char **argv) {
	// Setup
	Verilated::commandArgs(argc, argv);
	DIV_TB	*tb = new DIV_TB();
 
	tb->reset();
	// tb->opentrace("div_tb.vcd");
 
	// Now we're ready.  All we need to do to test the divide of two
	// numbers is to call the respective divide(), divs(), or divu()
	// functions.  The program will crash on an assert error if anything
	// goes wrong.
	tb->divu((unsigned)-1,10);
	tb->divide(125,7);
	// And give us an extra clock tick in-between each test for good
	// measure.
	tb->tick();
 
	// Some other gentle tests
	tb->divide(125,-7);
	tb->tick();
	tb->divu((1u<<31),7);
	// Now some boundary conditions
	tb->divu((7u<<29),(1u<<31));
	tb->tick();
	tb->divs(32768,0);
	tb->tick();
	tb->divu((1u<<31),0);
	tb->tick();
	tb->divs((1u<<30),0);
	tb->tick();
	//
	// Now we switch to a more thorough test set.  It's not complete, just
	// ... more thorough.
	for(int i=32767; i>=0; i--) {
		tb->divs((1u<<30),i);
		tb->tick();
	} for(int i=32767; i>=0; i--) {
		// tb->divu(-1, i);
		tb->divu((1u<<31), i);
		tb->tick();
	} for(int i=32767; i>=0; i--) {
		tb->divide(32768, i);
		tb->tick();
	}
 
	/*
	 * While random data is a nice test idea, the following just never
	 * really tested the divide unit thoroughly enough.
	 *
	tb->divide(rand(),rand()/2);
	tb->tick();
	tb->divide(rand(),rand()/2);
	tb->tick();
	tb->divide(rand(),rand()/2);
	tb->tick();
	tb->divide(rand(),rand()/2);
	*/
 
	// Any failures above will be captured with a failed assert.  If we
	// get here, it means things worked.  Close up shop ...
	//
	// This closes any potential trace file
	delete	tb;
 
	// And declare success
	printf("SUCCESS!\n");
	exit(EXIT_SUCCESS);
}
 
 

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.