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

Subversion Repositories wbscope

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /wbscope/trunk/bench
    from Rev 12 to Rev 13
    Reverse comparison

Rev 12 → Rev 13

/cpp/Makefile
1,11 → 1,30
################################################################################
##
## Filename:
## Filename: bench/cpp/Makefile
##
## Project: WBScope, a wishbone hosted scope
##
## Purpose:
## Purpose: This file directs the build of a Verilator-based test bench to
## prove that the wbscope and wbscopc work. This build must be
## called after building in bench/rtl, since it depends upon the products
## of that build.
##
## Targets:
##
## all: Builds both wbscope_tb and wbscopc_tb
##
## clean: Cleans up all of the build products, together with the .vcd
## files, so you can start over from scratch.
##
## wbscope_tb: A test bench for the basic wishbone scope.
## Prints success or failure on the last line.
##
## wbscopc_tb: A test bench for the compressed wishbone scope.
## Prints success or failure on the last line.
##
## test: Runs both testbenches, printing success if both succeed, or
## failure if one of the two does not.
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
31,19 → 50,30
## License: GPL, v3, as defined and found on www.gnu.org,
## http://www.gnu.org/licenses/gpl.html
##
##
################################################################################
##
##
all: wbscope_tb
all: wbscope_tb wbscopc_tb
CXX := g++
RTLD := ../rtl
ROBJD:= $(RTLD)/obj_dir
VROOT:= /usr/share/verilator
VINCS:= -I$(VROOT)
VERILATOR_ROOT ?= $(shell bash -c 'verilator -V|grep VERILATOR_ROOT| head -1|sed -e " s/^.*=\s*//"')
VROOT:= $(VERILATOR_ROOT)
INCS := -I$(VROOT)/include -I$(ROBJD)
VSRCS:= $(VROOT)/include/verilated.cpp $(VROOT)/include/verilated_vcd_c.cpp
TBOBJ:= $(ROBJD)/Vwbscope_tb__ALL.a
TCOBJ:= $(ROBJD)/Vwbscopc_tb__ALL.a
 
wbscope_tb: wbscope_tb.cpp $(ROBJD)/Vwbscope_tb__ALL.a $(ROBJD)/Vwbscope_tb.h
wbscope_tb: wbscope_tb.cpp $(TBOBJ) $(ROBJD)/Vwbscope_tb.h wb_tb.h testb.h
$(CXX) $(INCS) wbscope_tb.cpp $(VSRCS) $(TBOBJ) -o $@
 
wbscopc_tb: wbscopc_tb.cpp $(TCOBJ) $(ROBJD)/Vwbscopc_tb.h wb_tb.h testb.h
$(CXX) $(INCS) wbscopc_tb.cpp $(VSRCS) $(TCOBJ) -o $@
 
test: wbscope_tb wbscopc_tb
./wbscope_tb
./wbscopc_tb
 
clean:
rm -f wbscope_tb wbscopc_tb
rm -f wbscope_tb.vcd wbscopc_tb.vcd
/cpp/devbus.h
0,0 → 1,148
////////////////////////////////////////////////////////////////////////////////
//
// Filename: devbus.h
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: The purpose of this file is to document an interface which
// any device with a bus, whether it be implemented over a UART,
// an ethernet, or a PCI express bus, must implement. This describes
// only an interface, and not how that interface is to be accomplished.
//
// The neat part of this interface is that, if programs are designed to
// work with it, than the implementation details may be changed later
// and any program that once worked with the interface should be able
// to continue to do so. (i.e., switch from a UART controlled bus to a
// PCI express controlled bus, with minimal change to the software of
// interest.)
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef DEVBUS_H
#define DEVBUS_H
 
#include <stdio.h>
#include <unistd.h>
 
typedef unsigned int uint32;
 
class BUSERR {
public:
uint32 addr;
BUSERR(const uint32 a) : addr(a) {};
};
 
class DEVBUS {
public:
typedef uint32 BUSW;
 
virtual void kill(void) = 0;
virtual void close(void) = 0;
 
// Write a single value to a single address
// a is the address of the value to be read as it exists on the
// wishbone bus within the FPGA.
// v is the singular value to write to this address
virtual void writeio(const BUSW a, const BUSW v) = 0;
 
// Read a single value to a single address
// a is the address of the value to be read as it exists on the
// wishbone bus within the FPGA.
// This function returns the value read from the device wishbone
// at address a.
virtual BUSW readio(const BUSW a) = 0;
 
// Read a series of values from values from a block of memory
// a is the address of the value to be read as it exists on the
// wishbone bus within the FPGA.
// len is the number of words to read
// buf is a pointer to a place to store the words once read.
// This is equivalent to:
// for(int i=0; i<len; i++)
// buf[i] = readio(a+i);
// only it's faster in our implementation.
virtual void readi(const BUSW a, const int len, BUSW *buf) = 0;
 
// Read a series of values from the same address in memory. This
// call is identical to readi, save that the address is not incremented
// from one read to the next. It is equivalent to:
// for(int i=0; i<len; i++)
// buf[i] = readio(a);
// only it's faster in our implementation.
//
virtual void readz(const BUSW a, const int len, BUSW *buf) = 0;
 
// Write a series of values into a block of memory on the FPGA
// a is the address of the value to be written as it exists on the
// wishbone bus within the FPGA.
// len is the number of words to write
// buf is a pointer to a place to from whence to grab the data
// to be written.
// This is equivalent to:
// for(int i=0; i<len; i++)
// writeio(a+i, buf[i]);
// only it's faster in our implementation.
virtual void writei(const BUSW a, const int len, const BUSW *buf) = 0;
// Write a series of values into the same address on the FPGA bus. This
// call is identical to writei, save that the address is not incremented
// from one write to the next. It is equivalent to:
// for(int i=0; i<len; i++)
// writeio(a, buf[i]);
// only it's faster in our implementation.
//
virtual void writez(const BUSW a, const int len, const BUSW *buf) = 0;
 
// Query whether or not an interrupt has taken place
virtual bool poll(void) = 0;
 
// Sleep until interrupt, but sleep no longer than msec milliseconds
virtual void usleep(unsigned msec) = 0;
 
// Sleep until an interrupt, no matter how long it takes for that
// interrupt to take place
virtual void wait(void) = 0;
 
// Query whether or not a bus error has taken place. This is somewhat
// of a misnomer, as my current bus error detection code exits any
// interface, but ... it is what it is.
virtual bool bus_err(void) const = 0;
 
// Clear any bus error condition.
virtual void reset_err(void) = 0;
 
// Clear any interrupt condition that has already been noticed by
// the interface, does not check for further interrupt
virtual void clear(void) = 0;
 
virtual ~DEVBUS(void) { };
};
 
#endif
/cpp/testb.h
2,7 → 2,7
//
// Filename: testb.h
//
// Project: Zip CPU -- a small, lightweight, RISC CPU core
// Project: WBScope, a wishbone hosted scope
//
// Purpose: A wrapper for a common interface to a clocked FPGA core
// begin exercised in Verilator.
25,7 → 25,7
// 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
// 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.
//
34,6 → 34,8
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef TESTB_H
#define TESTB_H
 
41,6 → 43,8
#include <stdint.h>
#include <verilated_vcd_c.h>
 
#define TBASSERT(TB,A) do { if (!(A)) { (TB).closetrace(); } assert(A); } while(0);
 
template <class VA> class TESTB {
public:
VA *m_core;
50,22 → 54,27
TESTB(void) : m_trace(NULL), m_tickcount(0l) {
m_core = new VA;
Verilated::traceEverOn(true);
m_core->i_clk = 0;
eval(); // Get our initial values set properly.
}
virtual ~TESTB(void) {
if (m_trace) m_trace->close();
closetrace();
delete m_core;
m_core = NULL;
}
 
virtual void opentrace(const char *vcdname) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(vcdname);
if (!m_trace) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(vcdname);
}
}
 
virtual void closetrace(void) {
if (m_trace) {
m_trace->close();
delete m_trace;
m_trace = NULL;
}
}
77,16 → 86,22
virtual void tick(void) {
m_tickcount++;
 
//if((m_trace)&&(m_tickcount)) m_trace->dump(10*m_tickcount-4);
// Make sure we have our evaluations straight before the top
// of the clock. This is necessary since some of the
// connection modules may have made changes, for which some
// logic depends. This forces that logic to be recalculated
// before the top of the clock.
eval();
if ((m_trace)&&(m_tickcount)) m_trace->dump(10*m_tickcount-2);
if (m_trace) m_trace->dump(10*m_tickcount-2);
m_core->i_clk = 1;
eval();
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk = 0;
eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
if (m_trace) {
m_trace->dump(10*m_tickcount+5);
m_trace->flush();
}
}
 
virtual void reset(void) {
/cpp/wb_tb.h
0,0 → 1,455
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wb_tb.cpp
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: To provide a fairly generic interface wrapper to a wishbone bus,
// that can then be used to create a test-bench class.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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 <stdlib.h>
 
#include <verilated.h>
#include <verilated_vcd_c.h>
#include "testb.h"
#include "devbus.h"
 
const int BOMBCOUNT = 32;
 
template <class VA> class WB_TB : public TESTB<VA>, public DEVBUS {
#ifdef WBERR
bool m_buserr;
#endif
#ifdef INTERRUPTWIRE
bool m_interrupt;
#endif
public:
typedef uint32_t BUSW;
bool m_bomb;
 
WB_TB(void) {
m_bomb = false;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
#ifdef WBERR
m_buserr = false;
#endif
#ifdef INTERRUPTWIRE
m_interrupt = false;
#endif
}
 
virtual void close(void) {
TESTB<VA>::closetrace();
}
 
virtual void kill(void) {
close();
}
 
#ifdef INTERRUPTWIRE
virtual void tick(void) {
TESTB<VA>::tick();
if (TESTB<VA>::m_core->INTERRUPTWIRE)
m_interrupt = true;
}
#endif
#define TICK this->tick
 
void idle(const unsigned counts = 1) {
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
for(unsigned k=0; k<counts; k++) {
this->tick();
assert(!TESTB<VA>::m_core->o_wb_ack);
}
}
 
BUSW readio(BUSW a) {
int errcount = 0;
BUSW result;
 
// printf("WB-READM(%08x)\n", a);
 
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_stb = 1;
TESTB<VA>::m_core->i_wb_we = 0;
TESTB<VA>::m_core->i_wb_addr= (a>>2);
 
if (TESTB<VA>::m_core->o_wb_stall) {
while((errcount++ < BOMBCOUNT)&&(TESTB<VA>::m_core->o_wb_stall)) {
TICK();
#ifdef WBERR
if (TESTB<VA>::m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return -1;
}
#endif
}
} TICK();
 
TESTB<VA>::m_core->i_wb_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack)) {
TICK();
#ifdef WBERR
if (TESTB<VA>::m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return -1;
}
#endif
}
 
 
result = TESTB<VA>::m_core->o_wb_data;
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("WB/SR-BOMB: NO RESPONSE AFTER %d CLOCKS\n", errcount);
m_bomb = true;
} else if (!TESTB<VA>::m_core->o_wb_ack) {
printf("WB/SR-BOMB: NO ACK, NO TIMEOUT\n");
m_bomb = true;
}
TICK();
 
assert(!TESTB<VA>::m_core->o_wb_ack);
assert(!TESTB<VA>::m_core->o_wb_stall);
 
return result;
}
 
void readv(const BUSW a, int len, BUSW *buf, const int inc=1) {
int errcount = 0;
int THISBOMBCOUNT = BOMBCOUNT * len;
int cnt, rdidx;
 
printf("WB-READM(%08x, %d)\n", a, len);
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(TESTB<VA>::m_core->o_wb_stall))
TICK();
 
if (errcount >= BOMBCOUNT) {
printf("WB-READ(%d): Setting bomb to true (errcount = %d)\n", __LINE__, errcount);
m_bomb = true;
return;
}
 
errcount = 0;
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_stb = 1;
TESTB<VA>::m_core->i_wb_we = 0;
TESTB<VA>::m_core->i_wb_addr = (a>>2);
 
rdidx =0; cnt = 0;
 
do {
int s;
TESTB<VA>::m_core->i_wb_stb = ((rand()&7)!=0) ? 1:0;
s = ((TESTB<VA>::m_core->i_wb_stb)
&&(TESTB<VA>::m_core->o_wb_stall==0))?0:1;
TICK();
TESTB<VA>::m_core->i_wb_addr += (inc&(s^1))?4:0;
cnt += (s^1);
if (TESTB<VA>::m_core->o_wb_ack)
buf[rdidx++] = TESTB<VA>::m_core->o_wb_data;
#ifdef WBERR
if (TESTB<VA>::m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return -1;
}
#endif
} while((cnt < len)&&(errcount++ < THISBOMBCOUNT));
 
TESTB<VA>::m_core->i_wb_stb = 0;
 
while((rdidx < len)&&(errcount++ < THISBOMBCOUNT)) {
TICK();
if (TESTB<VA>::m_core->o_wb_ack)
buf[rdidx++] = TESTB<VA>::m_core->o_wb_data;
#ifdef WBERR
if (TESTB<VA>::m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return -1;
}
#endif
}
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
 
if(errcount >= THISBOMBCOUNT) {
printf("WB/PR-BOMB: NO RESPONSE AFTER %d CLOCKS\n", errcount);
m_bomb = true;
} else if (!TESTB<VA>::m_core->o_wb_ack) {
printf("WB/PR-BOMB: NO ACK, NO TIMEOUT\n");
m_bomb = true;
}
TICK();
assert(!TESTB<VA>::m_core->o_wb_ack);
}
 
void readi(const BUSW a, const int len, BUSW *buf) {
return readv(a, len, buf, 1);
}
 
void readz(const BUSW a, const int len, BUSW *buf) {
return readv(a, len, buf, 0);
}
 
void writeio(const BUSW a, const BUSW v) {
int errcount = 0;
 
printf("WB-WRITEM(%08x) <= %08x\n", a, v);
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_stb = 1;
TESTB<VA>::m_core->i_wb_we = 1;
TESTB<VA>::m_core->i_wb_addr= (a>>2);
TESTB<VA>::m_core->i_wb_data= v;
// TESTB<VA>::m_core->i_wb_sel = 0x0f;
 
if (TESTB<VA>::m_core->o_wb_stall)
while((errcount++ < BOMBCOUNT)&&(TESTB<VA>::m_core->o_wb_stall)) {
printf("Stalled, so waiting, errcount=%d\n", errcount);
TICK();
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
}
TICK();
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
 
TESTB<VA>::m_core->i_wb_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack)) {
TICK();
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
}
TICK();
 
// Release the bus?
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("WB/SW-BOMB: NO RESPONSE AFTER %d CLOCKS (LINE=%d)\n",errcount, __LINE__);
m_bomb = true;
} TICK();
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
assert(!TESTB<VA>::m_core->o_wb_ack);
assert(!TESTB<VA>::m_core->o_wb_stall);
}
 
void writev(const BUSW a, const int ln, const BUSW *buf, const int inc=1) {
unsigned errcount = 0, nacks = 0;
 
printf("WB-WRITEM(%08x, %d, ...)\n", a, ln);
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_stb = 1;
TESTB<VA>::m_core->i_wb_we = 1;
TESTB<VA>::m_core->i_wb_addr= (a>>2);
// TESTB<VA>::m_core->i_wb_sel = 0x0f;
for(unsigned stbcnt=0; stbcnt<ln; stbcnt++) {
// m_core->i_wb_addr= a+stbcnt;
TESTB<VA>::m_core->i_wb_data= buf[stbcnt];
errcount = 0;
 
while((errcount++ < BOMBCOUNT)&&(TESTB<VA>::m_core->o_wb_stall)) {
TICK();
if (TESTB<VA>::m_core->o_wb_ack)
nacks++;
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
}
// Tick, now that we're not stalled. This is the tick
// that gets accepted.
TICK();
if (TESTB<VA>::m_core->o_wb_ack) nacks++;
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
 
// Now update the address
TESTB<VA>::m_core->i_wb_addr += (inc)?4:0;
}
 
TESTB<VA>::m_core->i_wb_stb = 0;
 
errcount = 0;
while((nacks < ln)&&(errcount++ < BOMBCOUNT)) {
TICK();
if (TESTB<VA>::m_core->o_wb_ack) {
nacks++;
errcount = 0;
}
#ifdef WBERR
if (m_core->WBERR) {
m_buserr = true;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
return;
}
#endif
}
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("WB/PW-BOMB: NO RESPONSE AFTER %d CLOCKS (LINE=%d)\n",errcount,__LINE__);
m_bomb = true;
}
TICK();
assert(!TESTB<VA>::m_core->o_wb_ack);
assert(!TESTB<VA>::m_core->o_wb_stall);
}
 
void writei(const BUSW a, const int ln, const BUSW *buf) {
writev(a, ln, buf, 1);
}
 
void writez(const BUSW a, const int ln, const BUSW *buf) {
writev(a, ln, buf, 0);
}
 
 
bool bombed(void) const { return m_bomb; }
 
// bool debug(void) const { return m_debug; }
// bool debug(bool nxtv) { return m_debug = nxtv; }
 
bool poll(void) {
#ifdef INTERRUPTWIRE
return (m_interrupt)||(TESTB<VA>::m_core->INTERRUPTWIRE != 0);
#else
return false;
#endif
}
 
bool bus_err(void) const {
#ifdef WBERR
return m_buserr;
#else
return false;
#endif
}
 
void reset_err(void) {
#ifdef WBERR
m_buserr = false;;
#endif
}
 
void usleep(unsigned msec) {
#ifdef CLKRATEHZ
unsigned count = CLKRATEHZ / 1000 * msec;
#else
// Assume 100MHz if no clockrate is given
unsigned count = 1000*100 * msec;
#endif
while(count-- != 0)
#ifdef INTERRUPTWIRE
if (poll()) return; else
#endif
TICK();
}
 
void clear(void) {
#ifdef INTERRUPTWIRE
m_interrupt = false;
#endif
}
 
void wait(void) {
#ifdef INTERRUPTWIRE
while(!poll())
TICK();
#else
assert(("No interrupt defined",0));
#endif
}
};
 
/cpp/wbscopc_tb.cpp
0,0 → 1,195
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbscopc_tb.cpp
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: A quick test bench to determine if the run-length encoded
// wbscopc module works.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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 <verilated.h>
#include <verilated_vcd_c.h>
#include "Vwbscopc_tb.h"
#include "testb.h"
#include "devbus.h"
#define INTERRUPTWIRE o_interrupt
#include "wb_tb.h"
 
const int LGMEMSIZE = 15;
 
class WBSCOPC_TB : public WB_TB<Vwbscopc_tb> {
bool m_debug;
public:
 
WBSCOPC_TB(void) {
m_debug = true;
}
 
void tick(void) {
 
WB_TB<Vwbscopc_tb>::tick();
 
bool writeout = true;
if ((m_debug)&&(writeout)) {}
}
 
void reset(void) {
m_core->i_rst = 1;
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
tick();
m_core->i_rst = 0;
}
 
unsigned trigger(void) {
m_core->i_trigger = 1;
idle();
m_core->i_trigger = 0;
printf("TRIGGERED AT %08x\n", m_core->o_data);
return m_core->o_data;
}
 
bool debug(void) const { return m_debug; }
bool debug(bool nxtv) { return m_debug = nxtv; }
};
 
int main(int argc, char **argv) {
Verilated::commandArgs(argc, argv);
WBSCOPC_TB *tb = new WBSCOPC_TB;
unsigned v, addr, trigger_addr;
unsigned *buf;
int trigpt;
 
tb->opentrace("wbscopc_tb.vcd");
printf("Giving the core 2 cycles to start up\n");
// Before testing, let's give the unit time enough to warm up
tb->reset();
tb->idle(2);
 
#define WBSCOPE_STATUS 0
#define WBSCOPE_DATA 4
#define WBSCOPE_NORESET 0x80000000
#define WBSCOPE_TRIGGER (WBSCOPE_NO_RESET|0x08000000)
#define WBSCOPE_MANUAL (WBSCOPE_TRIGGER)
#define WBSCOPE_PRIMED 0x10000000
#define WBSCOPE_TRIGGERED 0x20000000
#define WBSCOPE_STOPPED 0x40000000
#define WBSCOPE_DISABLED 0x04000000
#define WBSCOPE_LGLEN(A) ((A>>20)&0x01f)
#define WBSCOPE_LENGTH(A) (1<<(LGLEN(A)))
 
// First test ... read the status register
v = tb->readio(WBSCOPE_STATUS);
int ln = WBSCOPE_LGLEN(v);
printf("V = %08x\n", v);
printf("LN = %d, or %d entries\n", ln, (1<<ln));
printf("DLY = %d\n", (v&0xfffff));
if (((1<<ln) < tb->m_tickcount)&&(v&0x10000000)) {
printf("SCOPE is already triggered! ??\n");
goto test_failure;
}
buf = new unsigned[(1<<ln)];
 
tb->idle((1<<(12+4)) + (1<<ln) +240);
 
v = tb->readio(WBSCOPE_STATUS);
if ((v&WBSCOPE_PRIMED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t primed! ??\n");
goto test_failure;
}
 
tb->trigger();
v = tb->readio(WBSCOPE_STATUS);
if ((v&WBSCOPE_TRIGGERED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t triggered! ??\n");
goto test_failure;
}
 
while((v & WBSCOPE_STOPPED)==0)
v = tb->readio(WBSCOPE_STATUS);
printf("SCOPE has stopped, reading data\n");
 
tb->readz(WBSCOPE_DATA, (1<<ln), buf);
addr = 0;
trigger_addr = 0xffffffff;
for(int i=0; i<(1<<ln); i++) {
if (buf[i] & 0x80000000)
addr += (buf[i]&0x7fffffff) + 1;
else {
if ((i > 0)&&(buf[i-1]&0x80000000))
printf(" [*****]:\n");
printf("%5d[%5d]: %08x", addr, i, buf[i]);
if (buf[i] & 0x40000000) {
printf(" <<--- TRIGGER!");
trigger_addr = addr;
} printf("\n");
 
addr++;
}
} if ((buf[(1<<ln)-1]&0x80000000))
printf(" [*****]:\n");
 
if (buf[(1<<ln)-1] & 0x80000000) {
printf("ERR: LAST VALUE IS A RUN, 0x%08x\n", buf[(1<<ln)-1]);
goto test_failure;
}
 
if (trigger_addr == 0xffffffff) {
printf("ERR: TRIGGER NOT FOUND IN THE DATA!\n");
goto test_failure;
}
 
printf("TRIGGER ADDRESS = %08x (%5d)\n", trigger_addr, trigger_addr);
printf("V = %08x\n", v & 0x0fffff);
printf("Difference = %08x (%5d)\n", addr - trigger_addr,
addr - trigger_addr);
if (addr - 1 - trigger_addr != (v & 0x0fffff)) {
printf("TRIGGER AT THE WRONG LOCATION!\n");
goto test_failure;
}
 
printf("SUCCESS!!\n");
delete tb;
exit(0);
test_failure:
printf("FAIL-HERE\n");
for(int i=0; i<4; i++)
tb->tick();
printf("TEST FAILED\n");
delete tb;
exit(-1);
}
/cpp/wbscope_tb.cpp
39,30 → 39,24
 
#include <verilated.h>
#include <verilated_vcd_c.h>
#include "Vwbscope_tb.h"
#include "testb.h"
#include "Vwbscope_tb.h"
#define INTERRUPTWIRE o_interrupt
#include "wb_tb.h"
 
#define MMUFLAG_RONW 8 // Read only (not writeable)
#define MMUFLAG_ACCS 4 // Accessed
#define MMUFLAG_CCHE 2 // Cachable
#define MMUFLAG_THSP 1 // Page has this context
const int LGMEMSIZE = 15;
 
const int BOMBCOUNT = 32,
LGMEMSIZE = 15;
 
class WBSCOPE_TB : public TESTB<Vwbscope_tb> {
bool m_bomb, m_miss, m_err, m_debug;
int m_last_tlb_index;
class WBSCOPE_TB : public WB_TB<Vwbscope_tb> {
bool m_bomb, m_debug;
public:
 
WBSCOPE_TB(void) {
m_debug = true;
m_last_tlb_index = 0;
}
 
void tick(void) {
 
TESTB<Vwbscope_tb>::tick();
WB_TB<Vwbscope_tb>::tick();
 
bool writeout = true;
if ((m_debug)&&(writeout)) {}
76,113 → 70,10
m_core->i_rst = 0;
}
 
void wb_tick(void) {
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
tick();
assert(!m_core->o_wb_ack);
}
 
unsigned wb_read(unsigned a) {
unsigned result;
 
printf("WB-READM(%08x)\n", a);
 
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr= (a>>2)&1;
 
// Dont need to check for stalls, since the wbscope never stalls
tick();
 
m_core->i_wb_stb = 0;
 
while(!m_core->o_wb_ack)
tick();
 
result = m_core->o_wb_data;
 
// Release the bus?
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
 
// Let the bus idle for one cycle
tick();
 
return result;
}
 
void wb_read(unsigned a, int len, unsigned *buf) {
int cnt, rdidx;
 
printf("WB-READM(%08x, %d)\n", a, len);
 
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr = (a>>2)&1;
 
rdidx =0; cnt = 0;
 
do {
tick();
// Normally, we'd increment the address here. For the
// scope, multiple reads only make sense if they are
// from the same address, hence we don't increment the
// address here
// m_core->i_wb_addr += inc;
cnt += 1;
if (m_core->o_wb_ack)
buf[rdidx++] = m_core->o_wb_data;
} while(cnt < len);
 
m_core->i_wb_stb = 0;
 
while(rdidx < len) {
tick();
if (m_core->o_wb_ack)
buf[rdidx++] = m_core->o_wb_data;
}
 
// Release the bus?
m_core->i_wb_cyc = 0;
 
tick();
assert(!m_core->o_wb_ack);
}
 
void wb_write(unsigned a, unsigned v) {
int errcount = 0;
 
printf("WB-WRITEM(%08x) <= %08x\n", a, v);
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 1;
m_core->i_wb_addr= (a>>2)&1;
m_core->i_wb_data= v;
 
tick();
m_core->i_wb_stb = 0;
 
while(!m_core->o_wb_ack) {
tick();
}
 
tick();
 
// Release the bus?
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
 
assert(!m_core->o_wb_ack);
}
 
unsigned trigger(void) {
m_core->i_trigger = 1;
wb_tick();
idle();
m_core->i_trigger = 0;
printf("TRIGGERED AT %08x\n", m_core->o_data);
return m_core->o_data;
}
 
196,13 → 87,13
unsigned v;
unsigned *buf;
int trigpt;
unsigned trigger_time, expected_first_value;
 
tb->opentrace("wbscope_tb.vcd");
printf("Giving the core 2 cycles to start up\n");
// Before testing, let's give the unit time enough to warm up
tb->reset();
for(int i=0; i<2; i++)
tb->wb_tick();
tb->idle(2);
 
#define WBSCOPE_STATUS 0
#define WBSCOPE_DATA 4
217,7 → 108,7
#define WBSCOPE_LENGTH(A) (1<<(LGLEN(A)))
 
// First test ... read the status register
v = tb->wb_read(WBSCOPE_STATUS);
v = tb->readio(WBSCOPE_STATUS);
int ln = WBSCOPE_LGLEN(v);
printf("V = %08x\n", v);
printf("LN = %d, or %d entries\n", ln, (1<<ln));
228,10 → 119,9
}
buf = new unsigned[(1<<ln)];
 
for(int i=0; i<(1<<ln); i++)
tb->wb_tick();
tb->idle(1<<ln);
 
v = tb->wb_read(WBSCOPE_STATUS);
v = tb->readio(WBSCOPE_STATUS);
if ((v&WBSCOPE_PRIMED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t primed! ??\n");
238,8 → 128,10
goto test_failure;
}
 
tb->trigger();
v = tb->wb_read(WBSCOPE_STATUS);
trigger_time = tb->trigger() & 0x7fffffff;
printf("TRIGGERED AT %08x\n", trigger_time);
 
v = tb->readio(WBSCOPE_STATUS);
if ((v&WBSCOPE_TRIGGERED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t triggered! ??\n");
247,17 → 139,21
}
 
while((v & WBSCOPE_STOPPED)==0)
v = tb->wb_read(WBSCOPE_STATUS);
v = tb->readio(WBSCOPE_STATUS);
printf("SCOPE has stopped, reading data\n");
 
tb->wb_read(WBSCOPE_DATA, (1<<ln), buf);
tb->readz(WBSCOPE_DATA, (1<<ln), buf);
for(int i=0; i<(1<<ln); i++) {
printf("%4d: %08x\n", i, buf[i]);
if ((i>0)&&(((buf[i]&0x7fffffff)-(buf[i-1]&0x7fffffff))!=1))
printf("%4d: %08x%s\n", i, buf[i],
(i== (1<<ln)-1-(v&0x0fffff)) ? " <<--- TRIGGER!":"");
if ((i>0)&&(((buf[i]&0x7fffffff)-(buf[i-1]&0x7fffffff))!=1)) {
printf("ERR: Scope data doesn't increment!\n");
printf("\tIn other words--its not matching the test signal\n");
goto test_failure;
}
}
 
trigpt = (1<<ln)-v&(0x0fffff);
trigpt = (1<<ln)-v&(0x0fffff)-1;
if ((trigpt >= 0)&&(trigpt < (1<<ln))) {
printf("Trigger value = %08x\n", buf[trigpt]);
if (((0x80000000 & buf[trigpt])==0)&&(trigpt>0)) {
269,6 → 165,14
}
}
 
expected_first_value = trigger_time + (v&0x0fffff) - (1<<ln);
if (buf[0] != expected_first_value) {
printf("Initial value = %08x\n", buf[0]);
printf("Expected: %08x\n", expected_first_value);
printf("ERR: WRONG STARTING-VALUE\n");
goto test_failure;
}
 
printf("SUCCESS!!\n");
delete tb;
exit(0);
/rtl/Makefile
1,15 → 1,14
################################################################################
#
# Filename: Makefile
#
# Project: WBScope, a wishbone hosted scope
#
# Purpose: This makefile builds a verilator simulation of the rtl
# testbenches necessary to test certain components of the
# wishbone scope using Verilator. It does not make the system within
## Icarus, Vivado or Quartus.
##
## Filename: Makefile
##
## Project: WBScope, a wishbone hosted scope
##
## Purpose: This makefile builds a verilator simulation of the rtl
## testbenches necessary to test certain components of both the
## wishbone scope and its RLE compressed brother using Verilator.
##
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
40,13 → 39,18
##
##
.PHONY: all
all: wbscope_tb
all: wbscope_tb wbscopc_tb
 
RTLD := ../../rtl
VOBJ := obj_dir
 
#
#
# Building the wbscope test bench
#
#
$(VOBJ)/Vwbscope_tb.cpp: $(RTLD)/wbscope.v wbscope_tb.v
verilator -trace -cc -y $(RTLD) wbscope_tb.v
verilator -Wall -O3 -trace -cc -y $(RTLD) wbscope_tb.v
$(VOBJ)/Vwbscope_tb.h: $(VOBJ)/Vwbscope_tb.cpp
 
$(VOBJ)/Vwbscope_tb__ALL.a: $(VOBJ)/Vwbscope_tb.cpp $(VOBJ)/Vwbscope_tb.h
55,6 → 59,21
.PHONY: wbscope_tb
wbscope_tb: $(VOBJ)/Vwbscope_tb__ALL.a
 
#
#
# Building the wbscopc test bench, for the compressed wbscope
#
#
$(VOBJ)/Vwbscopc_tb.cpp: $(RTLD)/wbscopc.v wbscopc_tb.v
verilator -Wall -O3 -trace -cc -y $(RTLD) wbscopc_tb.v
$(VOBJ)/Vwbscopc_tb.h: $(VOBJ)/Vwbscopc_tb.cpp
 
$(VOBJ)/Vwbscopc_tb__ALL.a: $(VOBJ)/Vwbscopc_tb.cpp $(VOBJ)/Vwbscopc_tb.h
make --no-print-directory --directory=$(VOBJ) -f Vwbscopc_tb.mk
 
.PHONY: wbscopc_tb
wbscopc_tb: $(VOBJ)/Vwbscopc_tb__ALL.a
 
# $(VOBJ)/Vaxiscope_tb.cpp: $(RTLD)/axiscope.v axiscope.v
# verilator -trace -cc -y $(RTLD) wbscope_tb.v
# $(VOBJ)/Vaxiscope_tb.h: $(VOBJ)/Vwbscope_tb.cpp
/rtl/wbscopc_tb.v
0,0 → 1,92
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbscopc_tb.v
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: This file is a test bench wrapper around the compressed
// wishbone scope, designed to create a "signal" which can then
// be scoped and proven. Unlike the case of the normal wishbone scope,
// this scope needs a test signal that has lots of idle time surrounded
// my sudden changes. We'll handle our sudden changes via a counter.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
module wbscope_tb(i_clk,
// i_rst is required by our test infrastructure, yet unused here
i_rst,
// The test data. o_data is internally generated here from
// o_counter, i_trigger is given externally
i_trigger, o_data, o_counter,
// Wishbone bus interaction
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
// wishbone bus outputs
o_wb_ack, o_wb_stall, o_wb_data,
// And our output interrupt
o_interrupt);
input i_clk, i_rst, i_trigger;
output wire [30:0] o_data;
output wire [29:0] o_counter;
//
input i_wb_cyc, i_wb_stb, i_wb_we;
input i_wb_addr;
input [31:0] i_wb_data;
//
output wire o_wb_ack;
output wire [31:0] o_wb_data;
output wire o_wb_stall;
//
output o_interrupt;
 
reg [29:0] counter;
initial counter = 0;
always @(posedge i_clk)
counter <= counter + 1'b1;
always @(posedge i_clk)
if (counter[11:8] == 4'h0)
o_data <= { i_trigger, counter };
else if ((counter[10])&&(counter[1]))
o_data <= { i_trigger, counter };
else
o_data <= { i_trigger, counter[29:12], 12'h0 };
 
wire wb_stall_ignored;
 
wbscopc #(.LGMEM(5'd14), .BUSW(32), .SYNCHRONOUS(1), .MAX_STEP(768),
.DEFAULT_HOLDOFF(36))
scope(i_clk, 1'b1, i_trigger, o_data,
i_clk, i_wb_cyc, i_wb_stb, i_wb_we,
i_wb_addr, i_wb_data,
o_wb_ack, wb_stall_ignored, o_wb_data,
o_interrupt);
 
assign o_wb_stall = 1'b0;
 
endmodule
/rtl/wbscope_tb.v
4,7 → 4,12
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose:
// Purpose: This file is a test bench wrapper around the wishbone scope,
// designed to create a "signal" which can then be scoped and
// proven. In our case here, the "signal" is a counter. When we test
// the scope within our bench/cpp Verilator testbench, we'll know if our
// test was "correct" if the counter 1) only ever counts by 1, and 2) if
// the trigger lands on thte right data sample.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
35,9 → 40,18
////////////////////////////////////////////////////////////////////////////////
//
//
module wbscope_tb(i_clk, i_rst, i_trigger, o_data,
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
o_wb_ack, o_wb_data, o_interrupt);
module wbscope_tb(i_clk,
// i_rst is required by our test infrastructure, yet unused here
i_rst,
// The test data. o_data is internally generated here from a
// counter, i_trigger is given externally
i_trigger, o_data,
// Wishbone bus interaction
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
// wishbone bus outputs
o_wb_ack, o_wb_stall, o_wb_data,
// And our output interrupt
o_interrupt);
input i_clk, i_rst, i_trigger;
output wire [31:0] o_data;
//
46,6 → 60,7
input [31:0] i_wb_data;
//
output wire o_wb_ack;
output wire o_wb_stall;
output wire [31:0] o_wb_data;
//
output o_interrupt;
59,7 → 74,8
 
wire wb_stall_ignored;
 
wbscope #(5'd6, 32, 1)
wbscope #(.LGMEM(5'd6), .BUSW(32), .SYNCHRONOUS(1),
.DEFAULT_HOLDOFF(1))
scope(i_clk, 1'b1, i_trigger, o_data,
i_clk, i_wb_cyc, i_wb_stb, i_wb_we,
i_wb_addr, i_wb_data,
66,4 → 82,10
o_wb_ack, wb_stall_ignored, o_wb_data,
o_interrupt);
 
assign o_wb_stall = 1'b0;
 
// verilator lint_off UNUSED
wire [1:0] unused;
assign unused = { i_rst, wb_stall_ignored };
// verilator lint_on UNUSED
endmodule

powered by: WebSVN 2.1.0

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