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

Subversion Repositories qspiflash

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /qspiflash/trunk/bench/cpp
    from Rev 15 to Rev 16
    Reverse comparison

Rev 15 → Rev 16

/Makefile
13,7 → 13,7
##
################################################################################
##
## Copyright (C) 2015-2016, 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
26,7 → 26,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.
##
38,36 → 38,82
##
##
CXX := g++
FLAGS := -Wall -Og -g
CFLAGS := -Wall -Og -g
OBJDIR := obj-pc
RTLD := ../../rtl
VERILATOR_ROOT ?= $(shell bash -c 'verilator -V|grep VERILATOR_ROOT | head -1 | sed -e " s/^.*=\s*//"')
VROOT := $(VERILATOR_ROOT)
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I$(VROOT)/include
SOURCES := eqspiflashsim.cpp eqspiflash_tb.cpp
VINCD := $(VROOT)/include
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I$(VINCD) -I$(VINCD)/vltstd
EQSOURCES := eqspiflashsim.cpp eqspiflash_tb.cpp
QSOURCES := qspiflashsim.cpp qspiflash_tb.cpp
SOURCES := $(EQSOURCES) $(QSOURCES)
VOBJDR := $(RTLD)/obj_dir
VLIB := $(VROOT)/include/verilated.cpp
RAWVLIB := verilated.cpp verilated_vcd_c.cpp
VSRCS := $(addprefix $(VROOT)/include/,$(RAWVLIB))
VOBJS := $(addprefix $(OBJDIR)/,$(subst .cpp,.o,$(RAWVLIB)))
QOBJS := $(addprefix $(OBJDIR)/,$(subst .cpp,.o,$(QSOURCES))) $(VOBJS)
EQOBJS := $(addprefix $(OBJDIR)/,$(subst .cpp,.o,$(EQSOURCES))) $(VOBJS)
SIMSRCS := enetctrlsim.cpp eqspiflashsim.cpp
all: $(OBJDIR)/ eqspiflash_tb pretest
all: qspiflash_tb pretest
 
$(OBJDIR)/:
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi"
 
$(OBJDIR)/%.o: %.cpp
$(CXX) $(FLAGS) -c $^ -o $@
$(mk-objdir)
$(CXX) $(CFLAGS) $(INCS) -c $< -o $@
 
eqspiflash_tb: eqspiflash_tb.cpp $(OBJDIR)/eqspiflashsim.o $(VOBJDR)/Veqspiflash__ALL.a
$(CXX) $(FLAGS) $(INCS) $^ $(VOBJDR)/Veqspiflash__ALL.a $(VLIB) -o $@
$(OBJDIR)/%.o: $(VINCD)/%.cpp
$(mk-objdir)
$(CXX) $(CFLAGS) $(INCS) -c $< -o $@
 
eqspiflash_tb: $(EQOBJS) $(VOBJDR)/Veqspiflash__ALL.a
$(CXX) $(CFLAGS) $(INCS) $(EQOBJS) $(VOBJDR)/Veqspiflash__ALL.a -o $@
 
qspiflash_tb: $(QOBJS) $(VOBJDR)/Vwbqspiflash__ALL.a $(VOBJS)
$(CXX) $(CFLAGS) $(INCS) $(QOBJS) $(VOBJDR)/Vwbqspiflash__ALL.a -o $@
 
.PHONY: pretest
pretest: eqspiflash_tb
pretest: qspiflash_tb eqspiflash_tb
@echo "The test bench has been created. Type make test, and look at"
@echo "the end of its output to see if it (still) works."
 
.PHONY: test
test: eqspiflash_tb
# .PHONY: test
# test: eqspiflash_tb
# ./eqspiflash_tb
 
.PHONY: test qtest eqtest
test: qtest eqtest
qtest: qspiflash_tb
./qspiflash_tb
eqtest: eqspiflash_tb qtest
./eqspiflash_tb
 
define mk-objdir
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi"
endef
 
 
define build-depends
@echo "Building dependency file(s)"
$(mk-objdir)
@$(CXX) $(CFLAGS) $(INCS) -MM $(SOURCES) > $(OBJDIR)/xdepends.txt
@sed -e 's/^.*.o: /$(OBJDIR)\/&/' < $(OBJDIR)/xdepends.txt > $(OBJDIR)/depends.txt
@rm $(OBJDIR)/xdepends.txt
endef
 
tags: $(SOURCES) $(HEADERS)
@echo "Generating tags"
@ctags $(SOURCES) $(HEADERS)
 
.PHONY: clean
clean:
rm ./eqspiflash_tb
rm -f ./eqspiflash_tb qspiflash_tb
rm -rf $(OBJDIR)/
 
.PHONY: depends
depends: tags
$(build-depends)
 
$(OBJDIR)/depends.txt: $(SOURCES) $(HEADERS)
$(build-depends)
 
-include $(OBJDIR)/depends.txt
/eqspiflash_tb.cpp
2,7 → 2,7
//
// Filename: eqspiflash_tb.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// Purpose: To determine whether or not the eqspiflash module works. Run
// this with no arguments, and check whether or not the last line
27,7 → 27,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.
//
39,6 → 39,7
//
//
#include "verilated.h"
#include "verilated_vcd_c.h"
#include "Veqspiflash.h"
#include "eqspiflashsim.h"
 
46,15 → 47,19
const int BOMBCOUNT = 2048;
 
class EQSPIFLASH_TB {
long m_tickcount;
unsigned long m_tickcount;
Veqspiflash *m_core;
EQSPIFLASHSIM *m_flash;
bool m_bomb;
VerilatedVcdC* m_trace;
 
public:
 
EQSPIFLASH_TB(void) {
Verilated::traceEverOn(true);
m_core = new Veqspiflash;
m_flash= new EQSPIFLASHSIM;
m_flash= new EQSPIFLASHSIM(24,true);
m_trace= NULL;
}
 
unsigned operator[](const int index) { return (*m_flash)[index]; }
65,12 → 70,24
m_flash->load(0,fname);
}
 
void trace(const char *fname) {
if (!m_trace) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(fname);
}
}
 
void tick(void) {
m_core->i_clk_200mhz = 1;
 
// m_core->i_clk_82mhz = 0;
// m_core->eval();
m_core->i_qspi_dat = (*m_flash)(m_core->o_qspi_cs_n,
m_core->o_qspi_sck, m_core->o_qspi_dat);
 
m_core->i_clk_82mhz = 1;
m_core->eval();
// #define DEBUGGING_OUTPUT
#ifdef DEBUGGING_OUTPUT
printf("%08lx-WB: %s %s/%s %s %s[%s%s%s%s%s] %s %s@0x%08x[%08x/%08x] -- SPI %s%s[%x/%x](%d,%d)",
m_tickcount,
(m_core->i_wb_cyc)?"CYC":" ",
171,7 → 188,7
(m_core->v__DOT__ctproc__DOT__accepted)?"CT-ACC":"");
 
 
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(m_core->v__DOT__preproc__DOT__pending)?" PENDING":"",
(m_core->v__DOT__preproc__DOT__lcl_key)?" KEY":"",
(m_core->v__DOT__preproc__DOT__ctreg_stb)?" CTSTB":"",
182,7 → 199,7
// (m_core->v__DOT__preproc__DOT__lcl_reg)?" LCLREG":"",
// (m_core->v__DOT__w_xip)?" XIP":"",
// (m_core->v__DOT__w_quad)?" QUAD":"",
// (m_core->v__DOT__bus_piperd)?" RDPIPE":"",
(m_core->v__DOT__bus_piperd)?" RDPIPE":"",
(m_core->v__DOT__preproc__DOT__wp)?" WRWP":"",
(m_core->v__DOT__ewproc__DOT__cyc)?" WRCYC":"",
(m_core->v__DOT__bus_pipewr)?" WRPIPE":"",
207,10 → 224,15
 
 
printf("\n");
#endif
 
if ((m_trace)&&(m_tickcount>0)) m_trace->dump(10*m_tickcount-2);
m_core->i_clk_82mhz = 1;
m_core->eval();
m_core->i_clk_200mhz = 0;
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk_82mhz = 0;
m_core->eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
m_tickcount++;
 
242,11 → 264,10
m_core->i_wb_we = 0;
m_core->i_wb_addr= a & 0x03fffff;
 
if (m_core->o_wb_stall)
if (m_core->o_wb_stall) {
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall))
tick();
else
tick();
} tick();
 
m_core->i_wb_data_stb = 0;
m_core->i_wb_ctrl_stb = 0;
263,7 → 284,7
m_core->i_wb_ctrl_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("SETTING ERR TO TRUE!!!!!\n");
printf("RD-SETTING ERR TO TRUE!!!!!\n");
m_bomb = true;
} else if (!m_core->o_wb_ack) {
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n");
324,7 → 345,7
m_core->i_wb_cyc = 0;
 
if(errcount >= THISBOMBCOUNT) {
printf("SETTING ERR TO TRUE!!!!! (errcount=%08x, THISBOMBCOUNT=%08x)\n", errcount, THISBOMBCOUNT);
printf("RDI-SETTING ERR TO TRUE!!!!! (errcount=%08x, THISBOMBCOUNT=%08x)\n", errcount, THISBOMBCOUNT);
m_bomb = true;
} else if (!m_core->o_wb_ack) {
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n");
361,7 → 382,7
m_core->i_wb_ctrl_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("SETTING ERR TO TRUE!!!!!\n");
printf("WB-SETTING ERR TO TRUE!!!!!\n");
m_bomb = true;
} tick();
}
404,7 → 425,7
m_core->i_wb_ctrl_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("SETTING ERR TO TRUE!!!!!\n");
printf("WBI-SETTING ERR TO TRUE!!!!!\n");
m_bomb = true;
} tick();
}
461,7 → 482,7
m_core->i_wb_ctrl_stb = 0;
 
if(errcount >= BOMBCOUNT) {
printf("SETTING ERR TO TRUE!!!!!\n");
printf("WBS-SETTING ERR TO TRUE!!!!!\n");
m_bomb = true;
} tick();
}
482,6 → 503,8
rdbuf = new unsigned[4096];
tb->setflash(0,0);
 
tb->trace("eqspi.vcd");
 
tb->wb_tick();
rdv = tb->wb_read(QSPIFLASH);
printf("READ[0] = %04x\n", rdv);
579,7 → 602,13
} printf("VECTOR TEST PASSES! (QUAD)\n");
 
printf("Attempting to switch to Quad mode with XIP\n");
tb->wb_write(3, tb->wb_read(3)|0x08);
{
int nv;
nv = tb->wb_read(3);
printf("READ VCONF = %02x\n", nv);
printf("WRITING VCONF= %02x\n", nv | 0x08);
tb->wb_write(3, nv|0x08);
}
// tb->wb_write(0, 0x22000000);
 
printf("Attempting to read in Quad mode, using XIP mode\n");
614,10 → 643,12
printf("Attempting to erase subsector 1\n");
tb->wb_write(0, 0xf20005be);
 
while (tb->wb_read(0)&0x01000000)
while((tb->wb_read(0)&0x01000000)&&(!tb->bombed()))
;
while(tb->wb_read(0)&0x80000000)
while((tb->wb_read(0)&0x80000000)&&(!tb->bombed()))
;
if (tb->bombed())
goto test_failure;
if (tb->wb_read(QSPIFLASH+1023) != rdbuf[0])
goto test_failure;
if (tb->wb_read(QSPIFLASH+2048) != rdbuf[1])
/eqspiflashsim.cpp
2,7 → 2,7
//
// Filename: eqspiflashsim.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// Purpose: This library simulates the operation of a Quad-SPI commanded
// flash, such as the Micron N25Q128A used on the Arty development
18,7 → 18,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, 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
31,7 → 31,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.
//
46,11 → 46,10
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
 
#include "eqspiflashsim.h"
 
#define MEMBYTES (1<<24)
 
static const unsigned
DEVESD = 0x014,
// MICROSECONDS = 200,
91,9 → 90,11
(char)0xf0, (char)0xef
};
 
EQSPIFLASHSIM::EQSPIFLASHSIM(void) {
const int NSECTORS = MEMBYTES>>16;
m_mem = new char[MEMBYTES];
EQSPIFLASHSIM::EQSPIFLASHSIM(const int lglen, bool debug) {
int nsectors;
m_membytes = (1<<lglen);
m_memmask = (m_membytes - 1);
m_mem = new char[m_membytes];
m_pmem = new char[256];
m_otp = new char[65];
for(int i=0; i<65; i++)
100,8 → 101,9
m_otp[i] = 0x0ff;
m_otp[64] = 1;
m_otp_wp = false;
m_lockregs = new char[NSECTORS];
for(int i=0; i<NSECTORS; i++)
nsectors = m_membytes>>16;
m_lockregs = new char[nsectors];
for(int i=0; i<nsectors; i++)
m_lockregs[i] = 0;
 
m_state = EQSPIF_IDLE;
110,39 → 112,50
m_ireg = m_oreg = 0;
m_sreg = 0x01c;
m_creg = 0x001; // Initial creg on delivery
m_vconfig = 0x7; // Volatile configuration register
m_vconfig = 0x83; // Volatile configuration register
m_nvconfig = 0x0fff; // Nonvolatile configuration register
m_quad_mode = false;
m_quad_mode = EQSPIF_QMODE_SPI;
m_mode_byte = 0;
m_flagreg = 0x0a5;
 
m_debug = true;
 
memset(m_mem, 0x0ff, MEMBYTES);
memset(m_mem, 0x0ff, m_membytes);
}
 
void EQSPIFLASHSIM::load(const unsigned addr, const char *fname) {
FILE *fp;
size_t len;
size_t len, nr = 0;
 
if (addr >= MEMBYTES)
if (addr >= m_membytes)
return; // return void
len = MEMBYTES-addr*4;
 
// If not given, then length is from the given address until the end
// of the flash memory
len = m_membytes-addr*4;
 
if (NULL != (fp = fopen(fname, "r"))) {
int nr = 0;
nr = fread(&m_mem[addr*4], sizeof(char), len, fp);
fclose(fp);
if (nr == 0) {
fprintf(stderr, "SPI-FLASH: Could not read %s\n", fname);
fprintf(stderr, "EQSPI-FLASH: Could not read %s\n", fname);
perror("O/S Err:");
}
} else {
fprintf(stderr, "SPI-FLASH: Could not open %s\n", fname);
fprintf(stderr, "EQSPI-FLASH: Could not open %s\n", fname);
perror("O/S Err:");
}
 
for(unsigned i=nr; i<m_membytes; i++)
m_mem[i] = 0x0ff;
}
 
void EQSPIFLASHSIM::load(const uint32_t offset, const char *data, const uint32_t len) {
uint32_t moff = (offset & (m_memmask));
 
memcpy(&m_mem[moff], data, len);
}
 
#define QOREG(A) m_oreg = ((m_oreg & (~0x0ff))|(A&0x0ff))
 
int EQSPIFLASHSIM::operator()(const int csn, const int sck, const int dat) {
182,13 → 195,13
*/
m_mem[(m_addr&(~0x0ff))+i] &= m_pmem[i];
}
m_quad_mode = false;
m_quad_mode = EQSPIF_QMODE_SPI;
} else if (EQSPIF_WRCR == m_state) {
if (m_debug) printf("Actually writing volatile config register\n");
if (m_debug) printf("Actually writing volatile config register: VCONFIG = 0x%04x\n", m_vconfig);
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07);
m_state = EQSPIF_IDLE;
} else if (EQSPIF_WRNVCONFIG == m_state) {
if (m_debug) printf("Actually writing nonvolatile config register\n");
if (m_debug) printf("Actually writing nonvolatile config register: VCONFIG = 0x%02x\n", m_nvconfig);
m_write_count = tWNVCR;
m_state = EQSPIF_IDLE;
} else if (EQSPIF_WREVCONFIG == m_state) {
240,7 → 253,7
m_sreg &= (~EQSPIF_WEL_FLAG);
m_sreg |= (EQSPIF_WIP_FLAG);
// Should I be checking the lock register(s) here?
for(int i=0; i<MEMBYTES; i++)
for(unsigned i=0; i<m_membytes; i++)
m_mem[i] = 0x0ff;
} else if (m_state == EQSPIF_PROGRAM_OTP) {
// Program the One-Time Programmable (OTP memory
263,19 → 276,15
m_write_count = tRES;
m_state = EQSPIF_IDLE;
*/
} else if (m_state == EQSPIF_QUAD_READ_CMD) {
m_state = EQSPIF_IDLE;
if (m_mode_byte!=0)
m_quad_mode = false;
else
m_state = EQSPIF_XIP;
} else if (m_state == EQSPIF_QUAD_READ) {
m_state = EQSPIF_IDLE;
if (m_mode_byte!=0)
m_quad_mode = false;
else
m_quad_mode = EQSPIF_QMODE_SPI;
else {
if (m_quad_mode == EQSPIF_QMODE_SPI_ADDR)
m_quad_mode = EQSPIF_QMODE_SPI;
m_state = EQSPIF_XIP;
// } else if (m_state == EQSPIF_XIP) {
}
}
 
m_oreg = 0x0fe;
322,9 → 331,12
if (m_count == 24) {
if (m_debug) printf("EQSPI: Entering from Quad-Read Idle to Quad-Read\n");
if (m_debug) printf("EQSPI: QI/O Idle Addr = %02x\n", m_ireg&0x0ffffff);
m_addr = (m_ireg) & 0x0ffffff;
assert((m_addr & 0xfc00000)==0);
m_addr = (m_ireg) & m_memmask;
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 24 + 4*8) {// After the idle bits
m_state = EQSPIF_QUAD_READ;
if (m_debug) printf("EQSPI: QI/O Dummy = %04x\n", m_ireg);
m_mode_byte = (m_ireg>>24) & 0x10;
} m_oreg = 0;
} else if (m_count == 8) {
QOREG(0x0a5);
377,8 → 389,10
if (m_debug) printf("EQSPI: WEL not set, cannot do a subsector erase\n");
m_state = EQSPIF_INVALID;
assert(0&&"WEL not set");
} else
} else {
m_state = EQSPIF_SUBSECTOR_ERASE;
if (m_debug) printf("EQSPI: SUBSECTOR_ERASE COMMAND\n");
}
break;
case 0x32: // QUAD Page program, 4 bits at a time
if (2 != (m_sreg & 0x203)) {
421,7 → 435,7
QOREG(m_evconfig);
break;
case 0x06b:
m_state = EQSPIF_QUAD_READ_CMD;
m_state = EQSPIF_QUAD_OREAD_CMD;
// m_quad_mode = true; // Not yet, need to wait past dummy registers
break;
case 0x70: // Read flag status register
479,10 → 493,8
if (m_debug) printf("EQSPI: READ LOCK REGISTER (Waiting on address)\n");
break;
case 0x0eb: // Here's the (other) read that we support
// printf("EQSPI: QUAD-I/O-READ\n");
// m_state = EQSPIF_QUAD_READ_CMD;
// m_quad_mode = true;
assert(0 && "Quad Input/Output fast read not supported");
m_state = EQSPIF_QUAD_IOREAD_CMD;
m_quad_mode = EQSPIF_QMODE_QSPI_ADDR;
break;
default:
printf("EQSPI: UNRECOGNIZED SPI FLASH CMD: %02x\n", m_ireg&0x0ff);
508,21 → 520,21
}
break;
case EQSPIF_WRCR: // Write volatile config register, 0x81
if (m_count == 8+8) {
if (m_count == 8+8+8) {
m_vconfig = m_ireg & 0x0ff;
printf("Setting volatile config register to %08x\n", m_vconfig);
if (m_debug) printf("Setting volatile config register to %08x\n", m_vconfig);
assert((m_vconfig & 0xfb)==0x8b);
} break;
case EQSPIF_WRNVCONFIG: // Write nonvolatile config register
if (m_count == 8+8) {
if (m_count == 8+8+8) {
m_nvconfig = m_ireg & 0x0ffdf;
printf("Setting nonvolatile config register to %08x\n", m_nvconfig);
if (m_debug) printf("Setting nonvolatile config register to %08x\n", m_nvconfig);
assert((m_nvconfig & 0xffc5)==0x8fc5);
} break;
case EQSPIF_WREVCONFIG: // Write enhanced volatile config reg
if (m_count == 8+8) {
m_evconfig = m_ireg & 0x0ff;
printf("Setting enhanced volatile config register to %08x\n", m_evconfig);
if (m_debug) printf("Setting enhanced volatile config register to %08x\n", m_evconfig);
assert((m_evconfig & 0x0d7)==0xd7);
} break;
case EQSPIF_WRLOCK:
530,14 → 542,13
m_addr = (m_ireg>>24)&0x0ff;
if ((m_lockregs[m_addr]&2)==0)
m_lockregs[m_addr] = m_ireg&3;
printf("Setting lock register[%02x] to %d\n", m_addr, m_lockregs[m_addr]);
assert((m_config & 0xfb)==0x8b);
if (m_debug) printf("Setting lock register[%02x] to %d\n", m_addr, m_lockregs[m_addr]);
} break;
case EQSPIF_RDLOCK:
if (m_count == 24) {
m_addr = (m_ireg>>16)&0x0ff;
QOREG(m_lockregs[m_addr]);
printf("Reading lock register[%02x]: %d\n", m_addr, m_lockregs[m_addr]);
if (m_debug) printf("Reading lock register[%02x]: %d\n", m_addr, m_lockregs[m_addr]);
} else
QOREG(m_lockregs[m_addr]);
break;
597,15 → 608,16
break;
case EQSPIF_FAST_READ:
if (m_count < 32) {
if (m_debug) printf("FAST READ, WAITING FOR FULL COMMAND (count = %d)\n", m_count);
QOREG(0x0c3);
} else if (m_count == 32) {
m_addr = m_ireg & 0x0ffffff;
m_addr = m_ireg & m_memmask;
if (m_debug) printf("FAST READ, ADDR = %08x\n", m_addr);
QOREG(0x0c3);
assert((m_addr & 0xf000003)==0);
if (m_addr & (~(m_memmask))) {
printf("EQSPI: ADDR = %08x ? !!\n", m_addr);
} assert((m_addr & (~(m_memmask)))==0);
} else if ((m_count >= 40)&&(0 == (m_sreg&0x01))) {
if (m_count == 40)
if ((m_debug)&&(m_count == 40))
printf("DUMMY BYTE COMPLETE ...\n");
QOREG(m_mem[m_addr++]);
if (m_debug) printf("SPIF[%08x] = %02x -> %02x\n", m_addr-1, m_mem[m_addr-1]&0x0ff, m_oreg);
614,51 → 626,56
if (m_debug) printf("CANNOT READ WHEN WRITE IN PROGRESS, m_sreg = %02x\n", m_sreg);
} else printf("How did I get here, m_count = %d\n", m_count);
break;
case EQSPIF_QUAD_READ_CMD:
// The command to go into quad read mode took 8 bits
// that changes the timings, else we'd use quad_Read
// below
case EQSPIF_QUAD_IOREAD_CMD:
if (m_count == 32) {
m_addr = m_ireg & 0x0ffffff;
// printf("FAST READ, ADDR = %08x\n", m_addr);
printf("EQSPI: QUAD READ, ADDR = %06x (%02x:%02x:%02x:%02x)\n", m_addr,
m_addr = m_ireg & m_memmask;
if (m_debug) printf("EQSPI: QUAD I/O READ, ADDR = %06x (%02x:%02x:%02x:%02x)\n", m_addr,
(m_addr<0x1000000)?(m_mem[m_addr]&0x0ff):0,
(m_addr<0x0ffffff)?(m_mem[m_addr+1]&0x0ff):0,
(m_addr<0x0fffffe)?(m_mem[m_addr+2]&0x0ff):0,
(m_addr<0x0fffffd)?(m_mem[m_addr+3]&0x0ff):0);
assert((m_addr & (~(MEMBYTES-1)))==0);
} else if (m_count == 32+8) {
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 8+24+8*4) {
QOREG(m_mem[m_addr++]);
m_quad_mode = true;
m_quad_mode = EQSPIF_QMODE_QSPI_ADDR;
m_mode_byte = (m_ireg & 0x080);
printf("EQSPI: (QUAD) MODE BYTE = %02x\n", m_mode_byte);
} else if ((m_count > 32+8)&&(0 == (m_sreg&0x01))) {
QOREG(m_mem[m_addr++]);
// printf("EQSPIF[%08x]/QR = %02x\n",
// m_addr-1, m_oreg);
m_state = EQSPIF_QUAD_READ;
} else {
// printf("ERR: EQSPIF--TRYING TO READ WHILE BUSY! (count = %d)\n", m_count);
m_oreg = 0;
}
break;
case EQSPIF_QUAD_OREAD_CMD:
if (m_count == 8+24) {
m_addr = m_ireg & m_memmask;
// printf("FAST READ, ADDR = %08x\n", m_addr);
if (m_debug) printf("EQSPI: QUAD READ, ADDR = %06x (%02x:%02x:%02x:%02x)\n", m_addr,
(m_addr<0x1000000)?(m_mem[m_addr]&0x0ff):0,
(m_addr<0x0ffffff)?(m_mem[m_addr+1]&0x0ff):0,
(m_addr<0x0fffffe)?(m_mem[m_addr+2]&0x0ff):0,
(m_addr<0x0fffffd)?(m_mem[m_addr+3]&0x0ff):0);
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 8+24+4*8) {
QOREG(m_mem[m_addr]);
m_quad_mode = EQSPIF_QMODE_SPI_ADDR;
m_mode_byte = (m_ireg & 0x080);
if (m_debug) printf("EQSPI: (QUAD) MODE BYTE = %02x\n", m_mode_byte);
m_state = EQSPIF_QUAD_READ;
}
break;
case EQSPIF_QUAD_READ:
if (m_count == 24+8*4) {// Requires 8 QUAD clocks
m_mode_byte = (m_ireg>>24) & 0x10;
printf("EQSPI/QR: MODE BYTE = %02x\n", m_mode_byte);
if ((m_count >= 64)&&(0 == (m_sreg&0x01))) {
QOREG(m_mem[m_addr++]);
} else if ((m_count >= 64)&&(0 == (m_sreg&0x01))) {
QOREG(m_mem[m_addr++]);
printf("EQSPIF[%08x]/QR = %02x\n", m_addr-1, m_oreg & 0x0ff);
// printf("EQSPIF[%08x]/QR = %02x\n", m_addr-1, m_oreg & 0x0ff);
} else {
m_oreg = 0;
printf("EQSPI/QR ... m_count = %d\n", m_count);
if (m_debug) printf("EQSPI/QR ... m_count = %d\n", m_count);
}
break;
case EQSPIF_PP:
if (m_count == 32) {
m_addr = m_ireg & 0x0ffffff;
m_addr = m_ireg & m_memmask;
if (m_debug) printf("EQSPI: PAGE-PROGRAM ADDR = %06x\n", m_addr);
assert((m_addr & 0xfc00000)==0);
assert((m_addr & (~(m_memmask)))==0);
// m_page = m_addr >> 8;
for(int i=0; i<256; i++)
m_pmem[i] = 0x0ff;
669,16 → 686,15
} break;
case EQSPIF_QPP:
if (m_count == 32) {
m_addr = m_ireg & 0x0ffffff;
m_quad_mode = true;
m_addr = m_ireg & m_memmask;
m_quad_mode = EQSPIF_QMODE_SPI_ADDR;
if (m_debug) printf("EQSPI/QR: PAGE-PROGRAM ADDR = %06x\n", m_addr);
assert((m_addr & 0xfc00000)==0);
// m_page = m_addr >> 8;
assert((m_addr & (~(m_memmask)))==0);
for(int i=0; i<256; i++)
m_pmem[i] = 0x0ff;
} else if (m_count >= 40) {
if (m_debug) printf("EQSPI: PROGRAM[%06x] = %02x\n", m_addr, m_ireg & 0x0ff);
m_pmem[m_addr & 0x0ff] = m_ireg & 0x0ff;
// printf("EQSPI/QR: PMEM[%02x] = 0x%02x -> %02x\n", m_addr & 0x0ff, m_ireg & 0x0ff, (m_pmem[(m_addr & 0x0ff)]&0x0ff));
m_addr = (m_addr & (~0x0ff)) | ((m_addr+1)&0x0ff);
} break;
case EQSPIF_SUBSECTOR_ERASE:
/eqspiflashsim.h
2,7 → 2,7
//
// Filename: eqspiflashsim.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// Purpose: This library simulates the operation of an Extended Quad-SPI
// commanded flash, such as the N25Q128A used on the Arty
14,7 → 14,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, 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
27,7 → 27,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.
//
63,7 → 63,8
EQSPIF_RDID,
EQSPIF_RELEASE,
EQSPIF_FAST_READ,
EQSPIF_QUAD_READ_CMD,
EQSPIF_QUAD_OREAD_CMD,
EQSPIF_QUAD_IOREAD_CMD,
EQSPIF_QUAD_READ,
EQSPIF_PP,
EQSPIF_QPP,
82,14 → 83,22
char *m_mem, *m_pmem, *m_otp, *m_lockregs;
int m_last_sck;
unsigned m_write_count, m_ireg, m_oreg, m_sreg, m_addr,
m_count, m_vconfig, m_mode_byte, m_creg,
m_nvconfig, m_evconfig, m_flagreg, m_nxtout[4];
bool m_quad_mode, m_debug, m_otp_wp;
m_count, m_vconfig, m_mode_byte, m_creg, m_membytes,
m_memmask, m_nvconfig, m_evconfig, m_flagreg, m_nxtout[4];
bool mode, m_debug, m_otp_wp;
 
typedef enum {
EQSPIF_QMODE_SPI = 0,
EQSPIF_QMODE_QSPI_ADDR,
EQSPIF_QMODE_SPI_ADDR
} QUAD_MODE;
QUAD_MODE m_quad_mode;
 
public:
EQSPIFLASHSIM(void);
EQSPIFLASHSIM(const int lglen = 24, bool debug = false);
void load(const char *fname) { load(0, fname); }
void load(const unsigned addr, const char *fname);
void load(const uint32_t offset, const char *data, const uint32_t len);
void debug(const bool dbg) { m_debug = dbg; }
bool debug(void) const { return m_debug; }
bool write_enabled(void) const { return m_debug; }
/qspiflash_tb.cpp
0,0 → 1,361
////////////////////////////////////////////////////////////////////////////////
//
// Filename: qspiflash_tb.cpp
//
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// Purpose: To determine whether or not the qspiflash module works. Run
// this with no arguments, and check whether or not the last line
// contains "SUCCESS" or not. If it does contain "SUCCESS", then the
// module passes all tests found within here.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, 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 "Vwbqspiflash.h"
#include "qspiflashsim.h"
#include "wbflash_tb.h"
 
#define QSPIFLASH 0x0400000
#define PARENT WBFLASH_TB<Vwbqspiflash>
 
class QSPIFLASH_TB : public PARENT {
QSPIFLASHSIM *m_flash;
bool m_bomb;
public:
 
QSPIFLASH_TB(void) {
m_core = new Vwbqspiflash;
m_flash= new QSPIFLASHSIM;
m_flash->debug(true);
}
 
unsigned operator[](const int index) { return (*m_flash)[index]; }
void setflash(unsigned addr, unsigned v) {
m_flash->set(addr, v);
}
void load(const char *fname) {
m_flash->load(0,fname);
}
 
void set(const unsigned addr, const unsigned val) {
m_flash->set(addr, val);
}
 
void tick(void) {
m_core->i_qspi_dat = (*m_flash)(m_core->o_qspi_cs_n,
m_core->o_qspi_sck, m_core->o_qspi_dat);
 
 
printf("%08lx-WB: %s %s/%s %s %s",
m_tickcount,
(m_core->i_wb_cyc)?"CYC":" ",
(m_core->i_wb_data_stb)?"DSTB":" ",
(m_core->i_wb_ctrl_stb)?"CSTB":" ",
(m_core->o_wb_stall)?"STALL":" ",
(m_core->o_wb_ack)?"ACK":" ");
printf(" %s@0x%08x[%08x/%08x]",
(m_core->i_wb_we)?"W":"R",
(m_core->i_wb_addr), (m_core->i_wb_data),
(m_core->o_wb_data));
printf(" QSPI:%x:%x/%02x/%02x/%2d",
m_core->i_qspi_dat, m_core->o_qspi_mod,
m_core->v__DOT__state,
m_core->v__DOT__lldriver__DOT__state,
m_core->v__DOT__lldriver__DOT__spi_len);
printf(" %08x/%08x", m_core->v__DOT__spi_in,
m_core->v__DOT__lldriver__DOT__r_input);
printf(" %d,%d,%d/%d,%08x%c",
m_core->v__DOT__spi_busy,
m_core->v__DOT__spi_valid,
m_core->v__DOT__spi_wr,
m_core->v__DOT__spi_len,
m_core->v__DOT__spi_out,
(m_core->v__DOT__write_in_progress)?'W':' ');
 
printf("\n");
 
PARENT::tick();
}
 
bool bombed(void) const { return m_bomb; }
 
};
 
#define ERASEFLAG 0x80000000
#define DISABLEWP 0x10000000
#define ENABLEWP 0x00000000
#define NPAGES 256
#define SZPAGEB 256
#define SZPAGEW (SZPAGEB>>2)
#define SECTORSZW (NPAGES * SZPAGEW)
#define SECTORSZB (NPAGES * SZPAGEB)
#define RDBUFSZ (NPAGES * SZPAGEW)
 
int main(int argc, char **argv) {
Verilated::commandArgs(argc, argv);
QSPIFLASH_TB *tb = new QSPIFLASH_TB;
const char *DEV_RANDOM = "/dev/urandom";
unsigned rdv;
unsigned *rdbuf;
 
tb->opentrace("qspi.vcd");
 
tb->load(DEV_RANDOM);
rdbuf = new unsigned[RDBUFSZ];
tb->setflash(0,0);
 
tb->tick();
rdv = tb->wb_read(0);
printf("READ[0] = %04x\n", rdv);
if (rdv != 0)
goto test_failure;
 
tb->tick();
if (tb->bombed())
goto test_failure;
 
for(int i=0; (i<1000)&&(!tb->bombed()); i++) {
unsigned tblv;
tblv = (*tb)[(i<<2)];
rdv = tb->wb_read(i<<2);
 
if(tblv != rdv) {
printf("BOMB(INITIAL/SINGLE-READ): READ[%08x] %08x, EXPECTED %08x\n",
(i<<2), rdv, tblv);
goto test_failure;
break;
} else printf("MATCH: %08x == %08x\n", rdv, tblv);
}
 
printf("SINGLE-READ TEST PASSES\n");
 
for(int i=0; i<1000; i++)
rdbuf[i] = -1;
tb->wb_read(1000, 1000, rdbuf);
if (tb->bombed())
goto test_failure;
for(int i=0; i<1000; i++) {
if ((*tb)[(i<<2)+1000] != rdbuf[i]) {
printf("BOMB: V-READ[%08x] %08x, EXPECTED %08x\n", 1000+i, rdv, (*tb)[i+1000]);
goto test_failure;
}
} if (tb->bombed())
goto test_failure;
printf("VECTOR TEST PASSES!\n");
 
// Read the status register
/*
printf("ID[%2d]-RG = %08x\n", 0, rdv = tb->wb_read(8+0));
if (rdv != 0x20ba1810) {
printf("BOMB: ID[%2d]-RG = %08x != %08x\n", 0, rdv,
0x20ba1810);
goto test_failure;
}
 
for(int i=1; i<5; i++)
printf("ID[%2d]-RG = %02x\n", i, tb->wb_read(8+i));
if (tb->bombed())
goto test_failure;
*/
 
 
printf("Attempting to switch in Quad mode\n");
// tb->wb_write(4, (tb->wb_read(4)&0x07f)); // Adjust EVconfig
 
for(int i=0; (i<1000)&&(!tb->bombed()); i++) {
unsigned tblv;
tblv = (*tb)[(i<<2)];
rdv = tb->wb_read((i<<2));
 
if(tblv != rdv) {
printf("BOMB: Q-READ/SINGLE %08x, EXPECTED %08x\n", rdv, tblv);
goto test_failure;
break;
} else printf("MATCH: %08x == %08x\n", rdv, tblv);
} tb->wb_read(1000, 1000, rdbuf);
if (tb->bombed())
goto test_failure;
for(int i=0; i<1000; i++) {
if ((*tb)[(i<<2)+1000] != rdbuf[i]) {
printf("BOMB: Q-READ/VECTOR %08x, EXPECTED %08x\n", rdv, (*tb)[i+1000]);
goto test_failure;
}
} printf("VECTOR TEST PASSES! (QUAD)\n");
 
printf("Attempting to switch to Quad mode with XIP\n");
tb->wb_write(3, tb->wb_read(3)|0x08);
// tb->wb_write(0, 0x22000000);
 
printf("Attempting to read in Quad mode, using XIP mode\n");
for(int i=0; (i<1000)&&(!tb->bombed()); i++) {
unsigned tblv;
tblv = (*tb)[(i<<2)];
rdv = tb->wb_read((i<<2));
 
if(tblv != rdv) {
printf("BOMB: Q-READ/XIP %08x, EXPECTED %08x\n", rdv, tblv);
goto test_failure;
break;
} else printf("MATCH: %08x == %08x\n", rdv, tblv);
}
 
// Try a vector read
tb->wb_read(1000, 1000, rdbuf);
if (tb->bombed())
goto test_failure;
for(int i=0; i<1000; i++) {
if ((*tb)[(i<<2)+1000] != rdbuf[i]) {
printf("BOMB: Q-READ/XIP/VECTOR %08x, EXPECTED %08x\n", rdv, (*tb)[i+1000]);
goto test_failure;
}
} printf("VECTOR TEST PASSES! (QUAD+XIP)\n");
 
rdbuf[0] = tb->wb_read(1023);
rdbuf[1] = tb->wb_read(2048);
 
 
// Make sure, for testing purposes, that the words preceeding the
// sector we are going to erase and following it don't look like they've
// already been erased.
if ((*tb)[SECTORSZW-1] == 0xffffffff)
tb->set(SECTORSZW, 0);
if ((*tb)[2*SECTORSZW] == 0xffffffff)
tb->set(2*SECTORSZW, 0);
 
printf("Turning off write-protect, calling WEL\n");
tb->wb_ctrl_write(0, DISABLEWP);
 
/*
if (tb->write_protect()) {
printf("WRITE PROTECT ISN\'T OFF YET, EVEN THOUGH WEL ISSUED\n");
goto test_failure;
} */
 
printf("Attempting to erase subsector 1\n");
tb->wb_ctrl_write(0, ERASEFLAG | (1*SECTORSZW));
 
/*
if (!tb->write_in_progress()) {
printf("BOMB: Write in progress is false!\n");
goto test_failure;
}
*/
 
while (tb->wb_ctrl_read(0)&ERASEFLAG)
;
 
/*
if (tb->write_in_progress()) {
printf("BOMB: No write in progress\n");
goto test_failure;
}
*/
 
printf("Checking that the erase was successful\n");
for(int i=SECTORSZB; i<SECTORSZB*2; i+=4) {
if ((*tb)[i] != 0xffffffff) {
printf("BOMB: Erase of [%08x] was unsuccessful, FLASH[%08x] = %08x\n", i, i, (*tb)[i]);
goto test_failure;
}
}
 
// Make sure we didn't erase anything else
if ((*tb)[SECTORSZB-4] == 0xffffffff) {
printf("BOMB: Post write check #2, the prior address changed\n");
goto test_failure;
} if ((*tb)[2*SECTORSZB] == 0xffffffff) {
printf("BOMB: Post write check #2, the next address changed\n");
goto test_failure;
}
 
if (tb->wb_read(SECTORSZB-4) != (*tb)[SECTORSZB-4]) {
printf("BOMB: Post write check #2, the prior address changed\n");
goto test_failure;
} if (tb->wb_read(2*SECTORSZB) != (*tb)[2*SECTORSZB]) {
printf("BOMB: Post write check #2, the next address changed\n");
goto test_failure;
}
 
 
 
printf("Test: Trying a single word write\n");
 
// Try to execute a single write
tb->wb_ctrl_write(0,DISABLEWP);
tb->wb_write(SECTORSZB, 0x12345678);
 
while (tb->wb_ctrl_read(0)&ERASEFLAG)
;
 
if (tb->wb_read(SECTORSZB) != 0x12345678) {
printf("BOMB: Single (not page) write result incorrect: %08x != 0x12345678\n", tb->wb_read(SECTORSZB));
goto test_failure;
}
 
 
// Let's load up a sectors worth of random data into our buffer
{
FILE *fp;
fp = fopen(DEV_RANDOM, "r");
assert(RDBUFSZ == fread(rdbuf, sizeof(unsigned), RDBUFSZ, fp));
fclose(fp);
rdbuf[0] = 0x12345678;
}
 
// Now, let's try writing this sector ... one page at a time.
for(int p=0; p<NPAGES; p++) {
 
printf("Writing page %d\n", p);
tb->wb_ctrl_write(0, DISABLEWP);
// if (tb->write_protect()) goto test_failure;
tb->wb_write(SECTORSZB+p*SZPAGEB, SZPAGEW, &rdbuf[p*SZPAGEW]);
 
while (tb->wb_ctrl_read(0)&ERASEFLAG)
;
 
printf("Checking page %d\n", p);
for(int i=0; i<SZPAGEW; i++) {
if (rdbuf[p*SZPAGEW+i] != (*tb)[SECTORSZB+p*SZPAGEB+(i<<2)]) {
printf("BOMB: Write check, Addr[%08x]\n", SECTORSZB+p*SZPAGEB+(i<<2));
goto test_failure;
}
}
}
 
printf("SUCCESS!!\n");
exit(EXIT_SUCCESS);
test_failure:
printf("FAIL-HERE\n");
for(int i=0; i<8; i++)
tb->tick();
printf("TEST FAILED\n");
exit(EXIT_FAILURE);
}
/qspiflashsim.cpp
238,7 → 238,6
} m_oreg = 0;
} else if (m_count == 8) {
QOREG(0x0a5);
// printf("SFLASH-CMD = %02x\n", m_ireg & 0x0ff);
// Figure out what command we've been given
if (m_debug) printf("SPI FLASH CMD %02x\n", m_ireg&0x0ff);
switch(m_ireg & 0x0ff) {
/qspiflashsim.h
78,10 → 78,14
void load(const char *fname) { load(0, fname); }
void load(const unsigned addr, const char *fname);
void load(const uint32_t offset, const char *data, const uint32_t len);
bool write_protect(void) { return ((m_sreg & QSPIF_WEL_FLAG)==0); }
bool write_in_progress(void) { return ((m_sreg | QSPIF_WIP_FLAG)!=0); }
bool xip_mode(void) { return (QSPIF_QUAD_READ_IDLE == m_state); }
bool quad_mode(void) { return m_quad_mode; }
void debug(const bool dbg) { m_debug = dbg; }
bool debug(void) const { return m_debug; }
unsigned operator[](const int index) {
unsigned char *cptr = (unsigned char *)&m_mem[index<<2];
unsigned char *cptr = (unsigned char *)&m_mem[index];
unsigned v;
v = (*cptr++);
v = (v<<8)|(*cptr++);
90,7 → 94,7
 
return v; }
void set(const unsigned addr, const unsigned val) {
unsigned char *cptr = (unsigned char *)&m_mem[addr<<2];
unsigned char *cptr = (unsigned char *)&m_mem[addr];
*cptr++ = (val>>24);
*cptr++ = (val>>16);
*cptr++ = (val>> 8);
/testb.h
0,0 → 1,110
////////////////////////////////////////////////////////////////////////////////
//
// Filename: testb.h
//
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// Purpose: A wrapper for a common interface to a clocked FPGA core
// begin exercised in Verilator.
//
// 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 TESTB_H
#define TESTB_H
 
#include <stdio.h>
#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;
VerilatedVcdC* m_trace;
unsigned long m_tickcount;
 
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();
delete m_core;
m_core = NULL;
}
 
virtual void opentrace(const char *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();
m_trace = NULL;
}
}
 
virtual void eval(void) {
m_core->eval();
}
 
virtual void tick(void) {
m_tickcount++;
 
// 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_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);
m_trace->flush();
}
}
 
virtual void reset(void) {
}
};
 
#endif
/wbflash_tb.h
0,0 → 1,383
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbflash_tb.cpp
//
// Project: Wishbone Controlled Quad SPI Flash Controller
//
// 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 <verilated.h>
#include <verilated_vcd_c.h>
#include "testb.h"
 
const int BOMBCOUNT = 2048,
LGMEMSIZE = 15;
 
template <class VA> class WBFLASH_TB : public TESTB<VA> {
bool m_ack_expected;
public:
bool m_bomb;
 
WBFLASH_TB(void) {
m_bomb = false;
m_ack_expected = false;
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
}
 
#define TICK tick
virtual void tick(void) {
// printf("WB-TICK\n");
TESTB<VA>::tick();
assert((TESTB<VA>::m_core->i_wb_cyc)
||(!TESTB<VA>::m_core->o_wb_ack));
}
 
unsigned wb_ctrl_read(unsigned a) {
int errcount = 0;
unsigned result;
 
// printf("WB-READM(%08x)\n", a);
 
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_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();
}
} TICK();
 
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack)) {
TICK();
}
 
 
result = TESTB<VA>::m_core->o_wb_data;
m_ack_expected = false;
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_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);
 
while(TESTB<VA>::m_core->o_wb_stall)
TICK();
// assert(!TESTB<VA>::m_core->o_wb_stall);
 
return result;
}
 
unsigned wb_read(unsigned a) {
int errcount = 0;
unsigned result;
 
// printf("WB-READM(%08x)\n", a);
 
TESTB<VA>::m_core->i_wb_cyc = 1;
TESTB<VA>::m_core->i_wb_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
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();
}
} TICK();
 
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack)) {
TICK();
}
 
 
result = TESTB<VA>::m_core->o_wb_data;
m_ack_expected = false;
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_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);
//
// #warning Core should not assert stall post-ack"
// But ... the QSPI flash driver ... does .. ???
// assert(!TESTB<VA>::m_core->o_wb_stall);
while(TESTB<VA>::m_core->o_wb_stall)
TICK();
 
return result;
}
 
void wb_read(unsigned a, int len, unsigned *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_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_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_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
TESTB<VA>::m_core->i_wb_we = 0;
TESTB<VA>::m_core->i_wb_addr = (a>>2);
 
rdidx =0; cnt = 0;
 
do {
int s;
s = (TESTB<VA>::m_core->o_wb_stall==0)?0:1;
TICK();
TESTB<VA>::m_core->i_wb_addr += inc&(s^1);
cnt += (s^1);
if (TESTB<VA>::m_core->o_wb_ack)
buf[rdidx++] = TESTB<VA>::m_core->o_wb_data;
} while((cnt < len)&&(errcount++ < THISBOMBCOUNT));
 
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_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;
}
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
m_ack_expected = false;
 
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 wb_ctrl_write(unsigned a, unsigned 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_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_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();
}
TICK();
 
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack))
TICK();
TICK();
 
// Release the bus?
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
m_ack_expected = false;
 
if(errcount >= BOMBCOUNT) {
printf("WB/SW-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);
 
while(TESTB<VA>::m_core->o_wb_stall)
TICK();
}
 
void wb_write(unsigned a, unsigned 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_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
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();
}
TICK();
 
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!TESTB<VA>::m_core->o_wb_ack))
TICK();
TICK();
 
// Release the bus?
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
m_ack_expected = false;
 
if(errcount >= BOMBCOUNT) {
printf("WB/SW-BOMB: NO RESPONSE AFTER %d CLOCKS (LINE=%d)\n",errcount, __LINE__);
m_bomb = true;
} TICK();
assert(!TESTB<VA>::m_core->o_wb_ack);
 
while(TESTB<VA>::m_core->o_wb_stall)
TICK();
assert(!TESTB<VA>::m_core->o_wb_stall);
}
 
void wb_write(unsigned a, unsigned int ln, unsigned *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_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
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++;
}
// Tick, now that we're not stalled. This is the tick
// that gets accepted.
TICK();
if (TESTB<VA>::m_core->o_wb_ack) nacks++;
 
// Now update the address
TESTB<VA>::m_core->i_wb_addr += inc;
}
 
TESTB<VA>::m_core->i_wb_data_stb = 1;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
 
errcount = 0;
while((nacks < ln)&&(errcount++ < BOMBCOUNT)) {
TICK();
if (TESTB<VA>::m_core->o_wb_ack) {
nacks++;
errcount = 0;
}
}
 
// Release the bus
TESTB<VA>::m_core->i_wb_cyc = 0;
TESTB<VA>::m_core->i_wb_data_stb = 0;
TESTB<VA>::m_core->i_wb_ctrl_stb = 0;
m_ack_expected = false;
 
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);
while(TESTB<VA>::m_core->o_wb_stall)
TICK();
}
 
bool bombed(void) const { return m_bomb; }
};
 

powered by: WebSVN 2.1.0

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