URL
https://opencores.org/ocsvn/qspiflash/qspiflash/trunk
Subversion Repositories qspiflash
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 15 to Rev 16
- ↔ Reverse comparison
Rev 15 → Rev 16
/qspiflash/trunk/Makefile
0,0 → 1,66
################################################################################ |
## |
## Filename: Makefile |
## |
## Project: Wishbone Controlled Quad SPI Flash Controller |
## |
## Purpose: This is the master Makefile for the project. It coordinates |
## the build of a Verilator test, "proving" that this core works |
## (to the extent that any simulated test "proves" anything). This |
## make file depends upon the proper setup of 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 |
## |
## |
################################################################################ |
## |
## |
all: rtl bench test |
SUBMAKE := $(MAKE) --no-print-directory -C |
|
.PHONY: doc |
doc: |
$(SUBMAKE) doc |
|
.PHONY: rtl |
rtl: |
$(SUBMAKE) rtl |
|
.PHONY: bench |
bench: rtl |
$(SUBMAKE) bench/cpp |
|
.PHONY: test |
test: bench |
$(SUBMAKE) bench/cpp test |
|
.PHONY: clean |
clean: |
$(SUBMAKE) rtl clean |
$(SUBMAKE) bench/cpp clean |
$(SUBMAKE) doc clean |
|
|
/qspiflash/trunk/bench/cpp/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 |
/qspiflash/trunk/bench/cpp/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]) |
/qspiflash/trunk/bench/cpp/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: |
/qspiflash/trunk/bench/cpp/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/trunk/bench/cpp/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); |
} |
/qspiflash/trunk/bench/cpp/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) { |
/qspiflash/trunk/bench/cpp/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); |
/qspiflash/trunk/bench/cpp/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 |
/qspiflash/trunk/bench/cpp/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; } |
}; |
|
/qspiflash/trunk/rtl/Makefile
1,4 → 1,4
##########################################################################/ |
################################################################################ |
## |
## Filename: Makefile |
## |
12,9 → 12,9
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
##########################################################################/ |
################################################################################ |
## |
## Copyright (C) 2015, 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,11 → 26,16
## 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 |
## http://www.gnu.org/licenses/gpl.html |
## |
## |
##########################################################################/ |
################################################################################ |
## |
## |
all: test |
40,12 → 45,13
VDIRFB:= $(FBDIR)/obj_dir |
|
.PHONY: test |
test: $(VDIRFB)/Veqspiflash__ALL.a |
test: $(VDIRFB)/Veqspiflash__ALL.a $(VDIRFB)/Vwbqspiflash__ALL.a |
# test: $(VDIRFB)/Vfastmaster__ALL.a |
|
$(VDIRFB)/Veqspiflash.h $(VDIRFB)/Veqspiflash.cpp $(VDIRFB)/Veqspiflash.mk: eqspiflash.v lleqspi.v |
$(VDIRFB)/Vwbqspiflash.h $(VDIRFB)/Vwbqspiflash.cpp $(VDIRFB)/Vwbqspiflash.mk: wbqspiflash.v llqspi.v |
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v |
verilator -cc $*.v |
verilator -trace -cc $*.v |
|
$(VDIRFB)/V%__ALL.a: $(VDIRFB)/V%.mk |
cd $(VDIRFB); make -f V$*.mk |
/qspiflash/trunk/rtl/eqspiflash.v
2,7 → 2,7
// |
// Filename: eqspiflash.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: Provide access to the flash device on an Arty, via the Extended |
// SPI interface. Reads and writes will use the QuadSPI interface |
90,7 → 90,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. |
// |
102,7 → 102,7
// |
// |
// `define QSPI_READ_ONLY |
module eqspiflash(i_clk_200mhz, i_rst, |
module eqspiflash(i_clk_82mhz, i_rst, |
// Incoming wishbone connection(s) |
// The two strobe lines allow the data to live on a |
// separate part of the master bus from the control |
120,7 → 120,7
// Debug the interface |
o_dbg); |
|
input i_clk_200mhz, i_rst; |
input i_clk_82mhz, i_rst; |
// Wishbone bus inputs |
input i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input [21:0] i_wb_addr; // 24 bits of addr space |
142,7 → 142,7
output wire [31:0] o_dbg; |
|
initial o_cmd_accepted = 1'b0; |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
o_cmd_accepted=((i_wb_data_stb)||(i_wb_ctrl_stb))&&(~o_wb_stall); |
// |
// lleqspi |
154,7 → 154,7
reg [1:0] spi_len; |
wire [31:0] spi_out; |
wire spi_valid, spi_busy, spi_stopped; |
lleqspi lowlvl(i_clk_200mhz, spi_wr, spi_hold, spi_word, spi_len, |
lleqspi lowlvl(i_clk_82mhz, spi_wr, spi_hold, spi_word, spi_len, |
spi_spd, spi_dir, spi_recycle, spi_out, spi_valid, spi_busy, |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat); |
assign spi_stopped = (o_qspi_cs_n)&&(~spi_busy)&&(~spi_wr); |
181,7 → 181,7
// Live parameters |
w_xip, w_quad, w_idloaded, w_leave_xip; |
reg bus_wip; |
qspibus preproc(i_clk_200mhz, i_rst, |
qspibus preproc(i_clk_82mhz, i_rst, |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, |
i_wb_we, i_wb_addr, i_wb_data, |
bus_wb_ack, bus_wb_stall, bus_wb_data, |
211,7 → 211,7
wire [31:0] rd_spi_word; |
wire [1:0] rd_spi_len; |
// |
readqspi rdproc(i_clk_200mhz, bus_readreq, bus_piperd, |
readqspi rdproc(i_clk_82mhz, bus_readreq, bus_piperd, |
bus_other_req, |
bus_addr, rd_bus_ack, |
rd_qspi_req, rd_qspi_grant, |
241,7 → 241,7
// |
wire w_ew_wip; |
// |
writeqspi ewproc(i_clk_200mhz, bus_wreq,bus_ereq, |
writeqspi ewproc(i_clk_82mhz, bus_wreq,bus_ereq, |
bus_pipewr, bus_endwr, |
bus_addr, bus_data, |
ew_bus_ack, ew_qspi_req, ew_qspi_grant, |
268,7 → 268,7
wire [31:0] ct_spi_word; |
wire [1:0] ct_spi_len; |
// |
ctrlspi ctproc(i_clk_200mhz, |
ctrlspi ctproc(i_clk_82mhz, |
bus_ctreq, bus_wr, bus_addr[3:0], bus_data, bus_sector, |
ct_qspi_req, ct_grant, |
ct_spi_wr, ct_spi_hold, ct_spi_word, ct_spi_len, |
299,7 → 299,7
// |
wire w_id_wip; |
// |
idotpqspi idotp(i_clk_200mhz, bus_idreq, |
idotpqspi idotp(i_clk_82mhz, bus_idreq, |
bus_wr, bus_pipewr, bus_addr[4:0], bus_data, id_bus_ack, |
id_qspi_req, id_qspi_grant, |
id_spi_wr, id_spi_hold, id_spi_word, id_spi_len, |
311,7 → 311,7
reg owned; |
reg [1:0] owner; |
initial owned = 1'b0; |
always @(posedge i_clk_200mhz) // 7 inputs (spi_stopped is the CE) |
always @(posedge i_clk_82mhz) // 7 inputs (spi_stopped is the CE) |
if ((~owned)&&(spi_stopped)) |
begin |
casez({rd_qspi_req,ew_qspi_req,id_qspi_req,ct_qspi_req}) |
338,7 → 338,7
assign ct_grant = (owned)&&(owner == 2'b11); |
|
// Module controller |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
case(owner) |
2'b00: begin |
spi_wr <= (owned)&&(rd_spi_wr); |
382,7 → 382,7
initial bus_wip = 1'b0; |
initial last_wip = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
begin |
bus_wip <= w_ew_wip || w_id_wip; |
last_wip <= bus_wip; |
391,7 → 391,7
|
|
// Now, let's return values onto the wb bus |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
begin |
// Ack our internal bus controller. This means the command was |
// accepted, and the bus can go on to looking for the next |
451,7 → 451,7
// |
reg pending, lcl_wrreq, lcl_ctreq, lcl_ack, ack, wp_err, wp; |
reg lcl_reg; |
reg [14:0] esector; |
reg [12:0] esector; |
reg [21:0] next_addr; |
|
|
583,7 → 583,7
wire new_req; |
assign new_req = (pending)&&(~last_pending); |
|
initial esector = 15'h00; |
initial esector = 13'h00; |
initial o_wrreq = 1'b0; |
initial o_erreq = 1'b0; |
initial wp_err = 1'b0; |
613,9 → 613,9
|
if (set_sector) |
begin |
esector[13:0] <= { o_data[23:14], 4'h0 }; |
esector[11:0] <= { o_data[21:14], 4'h0 }; |
wp <= (o_data[30])&&(new_req)||(wp)&&(~new_req); |
esector[14] <= o_data[28]; // Subsector |
esector[12] <= o_data[28]; // Subsector |
if (o_data[28]) |
begin |
esector[3:0] <= o_data[13:10]; |
679,10 → 679,10
end |
|
|
assign o_wb_data[31:0] = { i_wip, ~wp, i_quad, esector[14], |
assign o_wb_data[31:0] = { i_wip, ~wp, i_quad, esector[12], |
i_idloaded, wp_err, i_xip, i_spi_stopped, |
esector[13:0], 10'h00 }; |
assign o_sector = { esector[13:0], 8'h00 }; // 22 bits |
2'b00, esector[11:0], 10'h00 }; |
assign o_sector = { 2'b00, esector[11:0], 8'h00 }; // 22 bits |
assign o_other = (r_other)||(o_idreq); |
|
endmodule |
698,6 → 698,7
`define RD_XIP 4'h7 |
`define RD_GO_TO_IDLE 4'h8 |
`define RD_GO_TO_XIP 4'h9 |
`define RD_IDLE_QUAD_PORT 4'ha |
|
module readqspi(i_clk, i_readreq, i_piperd, i_other_req, i_addr, o_bus_ack, |
o_qspi_req, i_grant, |
746,11 → 747,16
`RD_IDLE: begin |
r_requested <= 1'b0; |
o_qspi_req <= 1'b0; |
o_spi_word <= { ((i_quad)? 8'h6B: 8'h0b), i_addr, 2'b00 }; |
// 0x0b is a fast read, uses all SPI protocol |
// 0x6b is a Quad output fast read, uses SPI cmd, |
// SPI address, QSPI data |
// 0xeb is a Quad I/O fast read, using SPI cmd, |
// QSPI address and data |
o_spi_word <= { ((i_quad)? 8'hEB: 8'h0b), i_addr, 2'b00 }; |
o_spi_wr <= 1'b0; |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_len <= (i_quad)? 2'b00 : 2'b11; |
r_xip <= (i_xip)&&(i_quad); |
r_leave_xip <= 1'b0; // Not in it, so can't leave it |
r_quad <= i_quad; |
763,15 → 769,30
o_spi_wr <= 1'b1; // Write the address |
o_qspi_req <= 1'b1; |
if (accepted) |
begin |
rd_state <= (r_quad) ? `RD_IDLE_QUAD_PORT : `RD_SLOW_DUMMY; |
o_spi_word[31:8] <= o_spi_word[23:0]; |
end end |
`RD_IDLE_QUAD_PORT: begin |
o_spi_wr <= 1'b1; // Write the command |
o_qspi_req <= 1'b1; |
o_spi_spd <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b10; |
|
// We haven't saved our address any where but in the |
// SPI word we just sent. Hence, let's just |
// grab it from there. |
if (accepted) |
rd_state <= `RD_SLOW_DUMMY; |
end |
`RD_SLOW_DUMMY: begin |
o_spi_wr <= 1'b1; // Write 8 dummy clocks |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; // Write 8 dummy clocks--this is the same for |
o_qspi_req <= 1'b1; // both Quad I/O, Quad O, and fast-read commands |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_word[31:24] <= (r_xip) ? 8'h00 : 8'hff; |
o_spi_len <= 2'b00; // 8 clocks = 8-bits |
o_spi_spd <= r_quad; |
o_spi_word[31:0] <= (r_xip) ? 32'h00 : 32'hffffffff; |
o_spi_len <= (r_quad)? 2'b11:2'b00; // 8 clocks |
if (accepted) |
rd_state <= (r_quad)?`RD_QUAD_READ_DATA |
: `RD_SLOW_READ_DATA; |
/qspiflash/trunk/rtl/flash_config.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: flashconfig.v |
// |
10,13 → 10,12
// the configuration only includes whether the flash is read only or not. |
// Other configuration options may be added later. |
// |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, 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 |
29,7 → 28,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. |
// |
37,8 → 36,9
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`ifndef FLASH_CONFIG_V |
`define FLASH_CONFIG_V |
// |
/qspiflash/trunk/rtl/lleqspi.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: lleqspi.v |
// |
10,12 → 10,12
// When not in use, unlike our previous SPI work, no bits will |
// toggle. |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// 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 |
28,7 → 28,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. |
// |
36,7 → 36,9
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define EQSPI_IDLE 3'h0 |
`define EQSPI_START 3'h1 |
`define EQSPI_BITS 3'h2 |
/qspiflash/trunk/rtl/llqspi.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: llqspi.v |
// |
10,12 → 10,12
// When not in use, unlike our previous SPI work, no bits will |
// toggle. |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, 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 |
28,7 → 28,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. |
// |
36,7 → 36,7
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
193,6 → 193,7
initial o_valid = 1'b0; |
initial o_busy = 1'b0; |
initial r_input = 31'h000; |
initial o_mod = `QSPI_MOD_SPI; |
always @(posedge i_clk) |
if ((state == `QSPI_IDLE)&&(o_sck)) |
begin |
200,16 → 201,16
o_valid <= 1'b0; |
o_busy <= 1'b0; |
o_mod <= `QSPI_MOD_SPI; |
r_word <= i_word; |
r_spd <= i_spd; |
r_dir <= i_dir; |
if (i_wr) |
begin |
r_word <= i_word; |
state <= `QSPI_START; |
r_spd <= i_spd; |
r_dir <= i_dir; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8; |
o_cs_n <= 1'b0; |
// o_sck <= 1'b1; |
o_busy <= 1'b1; |
o_sck <= 1'b1; |
end |
end else if (state == `QSPI_START) |
begin // We come in here with sck high, stay here 'til sck is low |
228,13 → 229,9
o_busy <= 1'b1; |
o_valid <= 1'b0; |
if (r_spd) |
begin |
o_dat <= r_word[31:28]; |
// r_word <= { r_word[27:0], 4'h0 }; |
end else begin |
else |
o_dat <= { 3'b110, r_word[31] }; |
// r_word <= { r_word[30:0], 1'b0 }; |
end |
end else if (~o_sck) |
begin |
o_sck <= 1'b1; |
276,6 → 273,16
// this state. Here we chose to either STOP or |
// continue and transmit more. |
o_sck <= (i_hold); // No clocks while holding |
r_spd <= i_spd; |
r_dir <= i_dir; |
if (i_spd) |
begin |
r_word <= { i_word[27:0], 4'h0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 - 6'h4; |
end else begin |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 - 6'h1; |
end |
if((~o_busy)&&(i_wr))// Acknowledge a new request |
begin |
state <= `QSPI_BITS; |
283,51 → 290,29
o_sck <= 1'b0; |
|
// Read the new request off the bus |
r_spd <= i_spd; |
r_dir <= i_dir; |
// Set up the first bits on the bus |
o_mod <= (i_spd) ? { 1'b1, i_dir } : `QSPI_MOD_SPI; |
if (i_spd) |
begin |
o_dat <= i_word[31:28]; |
r_word <= { i_word[27:0], 4'h0 }; |
// spi_len <= spi_len - 4; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h4; |
end else begin |
else |
o_dat <= { 3'b110, i_word[31] }; |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h1; |
end |
|
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
end else begin |
o_sck <= 1'b1; |
state <= (i_hold)?`QSPI_HOLDING : `QSPI_STOP; |
o_busy <= (~i_hold); |
end |
|
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
end else if (state == `QSPI_HOLDING) |
begin |
/qspiflash/trunk/rtl/wbqspiflash.v
55,45 → 55,45
`include "flash_config.v" |
`default_nettype none |
// |
`define WBQSPI_RESET 5'h0 |
`define WBQSPI_RESET_QUADMODE 5'h1 |
`define WBQSPI_IDLE 5'h2 |
`define WBQSPI_RDIDLE 5'h3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 5'h4 |
`define WBQSPI_RD_DUMMY 5'h5 |
`define WBQSPI_QRD_ADDRESS 5'h6 |
`define WBQSPI_QRD_DUMMY 5'h7 |
`define WBQSPI_READ_CMD 5'h8 |
`define WBQSPI_READ_DATA 5'h9 |
`define WBQSPI_WAIT_TIL_RDIDLE 5'h10 |
`define WBQSPI_READ_ID_CMD 5'h11 |
`define WBQSPI_READ_ID 5'h12 |
`define WBQSPI_READ_STATUS 5'h13 |
`define WBQSPI_READ_CONFIG 5'h14 |
`define WBQSPI_WAIT_TIL_IDLE 5'h15 |
`define WBQSPI_RESET 5'd0 |
`define WBQSPI_RESET_QUADMODE 5'd1 |
`define WBQSPI_IDLE 5'd2 |
`define WBQSPI_RDIDLE 5'd3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 5'd4 |
`define WBQSPI_RD_DUMMY 5'd5 |
`define WBQSPI_QRD_ADDRESS 5'd6 |
`define WBQSPI_QRD_DUMMY 5'd7 |
`define WBQSPI_READ_CMD 5'd8 |
`define WBQSPI_READ_DATA 5'd9 |
`define WBQSPI_WAIT_TIL_RDIDLE 5'd10 |
`define WBQSPI_READ_ID_CMD 5'd11 |
`define WBQSPI_READ_ID 5'd12 |
`define WBQSPI_READ_STATUS 5'd13 |
`define WBQSPI_READ_CONFIG 5'd14 |
`define WBQSPI_WAIT_TIL_IDLE 5'd15 |
// |
// |
`ifndef READ_ONLY |
// |
`define WBQSPI_WAIT_WIP_CLEAR 5'h16 |
`define WBQSPI_CHECK_WIP_CLEAR 5'h17 |
`define WBQSPI_CHECK_WIP_DONE 5'h18 |
`define WBQSPI_WEN 5'h19 |
`define WBQSPI_PP 5'h20 // Program page |
`define WBQSPI_QPP 5'h21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 5'h22 |
`define WBQSPI_WR_BUS_CYCLE 5'h23 |
`define WBQSPI_WRITE_STATUS 5'h24 |
`define WBQSPI_WRITE_CONFIG 5'h25 |
`define WBQSPI_ERASE_WEN 5'h26 |
`define WBQSPI_ERASE_CMD 5'h27 |
`define WBQSPI_ERASE_BLOCK 5'h28 |
`define WBQSPI_CLEAR_STATUS 5'h29 |
`define WBQSPI_IDLE_CHECK_WIP 5'h30 |
`define WBQSPI_WAIT_WIP_CLEAR 5'd16 |
`define WBQSPI_CHECK_WIP_CLEAR 5'd17 |
`define WBQSPI_CHECK_WIP_DONE 5'd18 |
`define WBQSPI_WEN 5'd19 |
`define WBQSPI_PP 5'd20 // Program page |
`define WBQSPI_QPP 5'd21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 5'd22 |
`define WBQSPI_WR_BUS_CYCLE 5'd23 |
`define WBQSPI_WRITE_STATUS 5'd24 |
`define WBQSPI_WRITE_CONFIG 5'd25 |
`define WBQSPI_ERASE_WEN 5'd26 |
`define WBQSPI_ERASE_CMD 5'd27 |
`define WBQSPI_ERASE_BLOCK 5'd28 |
`define WBQSPI_CLEAR_STATUS 5'd29 |
`define WBQSPI_IDLE_CHECK_WIP 5'd30 |
// |
`endif |
|
module wbqspiflash(i_clk_100mhz, |
module wbqspiflash(i_clk, |
// Internal wishbone connections |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we, |
i_wb_addr, i_wb_data, |
103,10 → 103,11
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
o_interrupt); |
parameter ADDRESS_WIDTH=22; |
input wire i_clk_100mhz; |
localparam AW = ADDRESS_WIDTH-2; |
input wire i_clk; |
// Wishbone, inputs first |
input wire i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input wire [(ADDRESS_WIDTH-3):0] i_wb_addr; |
input wire [(AW-1):0] i_wb_addr; |
input wire [31:0] i_wb_data; |
// then outputs |
output reg o_wb_ack; |
130,7 → 131,7
wire [3:0] w_qspi_dat; |
wire [1:0] w_qspi_mod; |
// wire [22:0] spi_dbg; |
llqspi lldriver(i_clk_100mhz, |
llqspi lldriver(i_clk, |
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir, |
spi_out, spi_valid, spi_busy, |
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat, |
166,13 → 167,15
endgenerate |
|
reg [7:0] last_status; |
reg [9:0] reset_counter; |
reg quad_mode_enabled; |
reg spif_cmd, spif_override; |
reg [31:0] spif_data; |
reg [4:0] state; |
reg spif_ctrl, spif_req; |
reg alt_cmd, alt_ctrl; |
wire [(ADDRESS_WIDTH-17):0] spif_sector; |
assign spif_sector = spif_addr[(ADDRESS_WIDTH-3):14]; |
assign spif_sector = spif_addr[(AW-1):14]; |
|
// assign o_debug = { spi_wr, spi_spd, spi_hold, state, spi_dbg }; |
|
183,9 → 186,13
initial spi_len = 2'b00; |
initial quad_mode_enabled = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk_100mhz) |
initial spif_override = 1'b1; |
initial spif_ctrl = 1'b0; |
always @(posedge i_clk) |
begin |
spif_override <= 1'b0; |
alt_cmd <= (reset_counter[9:8]==2'b10)?reset_counter[3]:1'b1; // Toggle CS_n |
alt_ctrl <= (reset_counter[9:8]==2'b10)?reset_counter[0]:1'b1; // Toggle clock too |
if (state == `WBQSPI_RESET) |
begin |
// From a reset, we should |
203,7 → 210,8
state <= `WBQSPI_RESET_QUADMODE; |
spif_req <= 1'b0; |
spif_override <= 1'b1; |
last_status <= 8'hfc; // |
last_status <= 8'h00; // |
reset_counter <= 10'h3fc; // |
// This guarantees that we aren't starting in quad |
// I/O mode, where the FPGA configuration scripts may |
// have left us. |
219,15 → 227,20
o_wb_stall <= 1'b1; |
|
// Do something ... |
if (last_status == 8'h00) |
if (reset_counter == 10'h00) |
begin |
spif_override <= 1'b0; |
state <= `WBQSPI_IDLE; |
|
// Find out if we can use Quad I/O mode ... |
state <= `WBQSPI_READ_CONFIG; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; |
spi_in <= { 8'h35, 24'h00}; |
|
end else begin |
last_status <= last_status - 8'h1; |
reset_counter <= reset_counter - 10'h1; |
spif_override <= 1'b1; |
spif_cmd <= last_status[3]; // Toggle CS_n |
spif_ctrl <= last_status[0]; // Toggle clock too |
end |
end else if (state == `WBQSPI_IDLE) |
begin |
311,10 → 324,10
o_wb_stall <= 1'b1; |
case(i_wb_addr[1:0]) |
2'b00: begin // Erase command register |
write_protect <= ~i_wb_data[28]; |
write_protect <= !i_wb_data[28]; |
o_wb_stall <= 1'b0; |
|
if((i_wb_data[31])&&(~write_in_progress)) |
if((i_wb_data[31])&&(!write_in_progress)) |
begin |
// Command an erase--ack it immediately |
|
368,7 → 381,7
end |
endcase |
`endif |
end else if (i_wb_ctrl_stb) // &&(~i_wb_we)) |
end else if (i_wb_ctrl_stb) // &&(!i_wb_we)) |
begin |
case(i_wb_addr[1:0]) |
2'b00: begin // Read local register |
444,7 → 457,7
spi_hold <= 1'b0; |
spi_spd<= 1'b1; |
spi_dir <= 1'b0; // Write (for now) |
if ((i_wb_data_stb)&&(~i_wb_we)) |
if ((i_wb_data_stb)&&(!i_wb_we)) |
begin // Continue our read ... send the new address / mode |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
451,7 → 464,7
spi_len <= 2'b10; // Write address, but not mode byte |
spi_in <= { w_wb_addr, 8'ha0 }; |
state <= `WBQSPI_QRD_DUMMY; |
end else if((i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
end else if((i_wb_ctrl_stb)&&(!i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
begin |
// A local read that doesn't touch the device, so leave |
// the device in its current state |
1030,7 → 1043,7
// Do nothing here. |
end else if ((i_wb_data_stb)&&(i_wb_we) |
&&(i_wb_addr == (spif_addr+1)) |
&&(i_wb_addr[(ADDRESS_WIDTH-3):6]==spif_addr[(ADDRESS_WIDTH-3):6])) |
&&(i_wb_addr[(AW-1):6]==spif_addr[(AW-1):6])) |
begin |
spif_cmd <= 1'b1; |
spif_data <= i_wb_data; |
1103,7 → 1116,7
// together with setting our copy of the WIP bit |
write_in_progress <= 1'b1; |
// keeping track of which sector we just erased |
erased_sector <= spif_data[(ADDRESS_WIDTH-3):14]; |
erased_sector <= spif_data[(AW-1):14]; |
// and marking this erase sector as no longer dirty |
dirty_sector <= 1'b0; |
|
1121,11 → 1134,10
o_wb_ack <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
// When the port clears, we can head back to idle |
// No ack necessary, we ackd before getting |
// here. |
if ((~spi_busy)&&(~spi_wr)) |
begin |
o_wb_ack <= spif_req; |
state <= `WBQSPI_IDLE; |
end |
end else if (state == `WBQSPI_CLEAR_STATUS) |
begin // Issue a clear status command |
spi_wr <= 1'b1; |
1185,8 → 1197,8
end |
|
// Command and control during the reset sequence |
assign o_qspi_cs_n = (spif_override)?spif_cmd :w_qspi_cs_n; |
assign o_qspi_sck = (spif_override)?spif_ctrl:w_qspi_sck; |
assign o_qspi_mod = (spif_override)? 2'b01 :w_qspi_mod; |
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat; |
assign o_qspi_cs_n = (spif_override)?alt_cmd :w_qspi_cs_n; |
assign o_qspi_sck = (spif_override)?alt_ctrl:w_qspi_sck; |
assign o_qspi_mod = (spif_override)? 2'b01 :w_qspi_mod; |
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat; |
endmodule |