URL
https://opencores.org/ocsvn/qspiflash/qspiflash/trunk
Subversion Repositories qspiflash
Compare Revisions
- This comparison shows the changes necessary to convert path
/qspiflash/trunk
- from Rev 11 to Rev 12
- ↔ Reverse comparison
Rev 11 → Rev 12
/bench/cpp/eqspiflash_tb.cpp
0,0 → 1,727
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: eqspiflash_tb.cpp |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: To determine whether or not the eqspiflash 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 "Veqspiflash.h" |
#include "eqspiflashsim.h" |
|
#define QSPIFLASH 0x0400000 |
const int BOMBCOUNT = 2048; |
|
class EQSPIFLASH_TB { |
long m_tickcount; |
Veqspiflash *m_core; |
EQSPIFLASHSIM *m_flash; |
bool m_bomb; |
public: |
|
EQSPIFLASH_TB(void) { |
m_core = new Veqspiflash; |
m_flash= new EQSPIFLASHSIM; |
} |
|
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 tick(void) { |
m_core->i_clk_200mhz = 1; |
|
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[%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":" ", |
(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":" ", |
(m_core->v__DOT__bus_wb_ack)?"BS":" ", |
(m_core->v__DOT__rd_data_ack)?"RD":" ", |
(m_core->v__DOT__ew_data_ack)?"EW":" ", |
(m_core->v__DOT__id_data_ack)?"ID":" ", |
(m_core->v__DOT__ct_data_ack)?"CT":" ", |
(m_core->o_cmd_accepted)?"BUS":" ", |
(m_core->i_wb_we)?"W":"R", |
(m_core->i_wb_addr), (m_core->i_wb_data), |
(m_core->o_wb_data), |
(!m_core->o_qspi_cs_n)?"CS":" ", |
(m_core->o_qspi_sck)?"CK":" ", |
(m_core->o_qspi_dat), (m_core->i_qspi_dat), |
(m_core->o_qspi_dat)&1, ((m_core->i_qspi_dat)&2)?1:0); |
|
/// printf("%08lx-EQ: ", m_tickcount); |
printf("EQ: "); |
if (m_core->v__DOT__owned) { |
switch(m_core->v__DOT__owner&3) { |
case 0: printf("RD"); break; |
case 1: printf("EW"); break; |
case 2: printf("ID"); break; |
case 3: printf("CT"); break; |
} |
} else printf(" "); |
|
printf(" REQ[%s%s%s%s]", |
(m_core->v__DOT__rd_qspi_req)?"RD":" ", |
(m_core->v__DOT__ew_qspi_req)?"EW":" ", |
(m_core->v__DOT__id_qspi_req)?"ID":" ", |
(m_core->v__DOT__ct_qspi_req)?"CT":" "); |
|
printf(" %s[%s%2d%s%s0x%08x]", |
(m_core->v__DOT__spi_wr)?"CMD":" ", |
(m_core->v__DOT__spi_hold)?"HLD":" ", |
(m_core->v__DOT__spi_len+1)*8, |
(m_core->v__DOT__spi_dir)?"RD":"WR", |
(m_core->v__DOT__spi_spd)?"Q":" ", |
(m_core->v__DOT__spi_word)); |
|
printf(" STATE[%2x%s,%2x%s,%2x%s,%2x%s]", |
m_core->v__DOT__rdproc__DOT__rd_state, |
(m_core->v__DOT__rd_spi_wr)?"W":" ", |
m_core->v__DOT__ewproc__DOT__wr_state, |
(m_core->v__DOT__ew_spi_wr)?"W":" ", |
m_core->v__DOT__idotp__DOT__id_state, |
(m_core->v__DOT__id_spi_wr)?"W":" ", |
m_core->v__DOT__ctproc__DOT__ctstate, |
(m_core->v__DOT__ct_spi_wr)?"W":" "); |
|
printf(" LL:[%s%s%d(%08x)->%08x]", |
(m_core->v__DOT__spi_busy)?"BSY":" ", |
(m_core->v__DOT__spi_valid)?"VAL":" ", |
(m_core->v__DOT__lowlvl__DOT__state), |
(m_core->v__DOT__lowlvl__DOT__r_input), |
(m_core->v__DOT__spi_out)); |
|
// printf(" 0x%08x,%02x ", m_core->v__DOT__id_data, |
// m_core->v__DOT__bus_addr); |
printf(" %s%08x@%08x", |
(m_core->v__DOT__bus_wr)?"W":"R", |
m_core->v__DOT__bus_data, m_core->v__DOT__bus_addr); |
|
if (m_core->v__DOT__idotp__DOT__id_state == 5) |
printf(" %s[%2x]%s", |
(m_core->v__DOT__idotp__DOT__last_addr)?"LST":" ", |
(m_core->v__DOT__idotp__DOT__lcl_id_addr), |
(m_core->v__DOT__idotp__DOT__id_loaded)?"LOD":" "); |
|
|
printf(" %s[%08x]", |
(m_core->v__DOT__idotp__DOT__nxt_data_ack) |
?"NXT":" ", m_core->v__DOT__idotp__DOT__nxt_data); |
printf(" %s[%x]", |
(m_core->v__DOT__idotp__DOT__set_val)?"SET":" ", |
(m_core->v__DOT__idotp__DOT__set_addr)); |
|
printf(" RD:IACK[%x]", |
(m_core->v__DOT__rdproc__DOT__invalid_ack_pipe)); |
printf(" CT:IACK[%x]", |
(m_core->v__DOT__ctproc__DOT__invalid_ack_pipe)); |
|
{ |
unsigned counts = m_flash->counts_till_idle(); |
if (counts) |
printf(" %8dI ", counts); |
} |
printf("%s%s%s%s", |
(m_core->v__DOT__rdproc__DOT__accepted)?"RD-ACC":"", |
(m_core->v__DOT__ewproc__DOT__accepted)?"EW-ACC":"", |
(m_core->v__DOT__idotp__DOT__accepted)?"ID-ACC":"", |
(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", |
(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":"", |
(m_core->v__DOT__bus_ctreq)?" BUSCTRL":"", |
(m_core->v__DOT__bus_other_req)?" BUSOTHER":"", |
(m_core->v__DOT__preproc__DOT__wp)?" WP":"", |
(m_core->v__DOT__bus_wip)?" WIP":"", |
// (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__preproc__DOT__wp)?" WRWP":"", |
(m_core->v__DOT__ewproc__DOT__cyc)?" WRCYC":"", |
(m_core->v__DOT__bus_pipewr)?" WRPIPE":"", |
(m_core->v__DOT__bus_endwr)?" ENDWR":"", |
(m_core->v__DOT__ct_ack)?" CTACK":"", |
(m_core->v__DOT__rd_bus_ack)?" RDACK":"", |
(m_core->v__DOT__id_bus_ack)?" IDACK":"", |
(m_core->v__DOT__ew_bus_ack)?" EWACK":"", |
(m_core->v__DOT__preproc__DOT__lcl_ack)?" LCLACK":"", |
(m_core->v__DOT__rdproc__DOT__r_leave_xip)?" LVXIP":"", |
(m_core->v__DOT__preproc__DOT__new_req)?" NREQ":""); |
|
printf("%s%s%s", |
(m_core->v__DOT__bus_idreq)?" BUSID":"", |
(m_core->v__DOT__id_bus_ack)?" BUSAK":"", |
(m_core->v__DOT__idotp__DOT__id_read_request)?" IDRD":""); |
|
if (m_core->v__DOT__rdproc__DOT__r_requested) |
fputs(" RD:R_REQUESTED", stdout); |
if (m_core->v__DOT__rdproc__DOT__r_leave_xip) |
fputs(" RD:R_LVXIP", stdout); |
|
|
printf("\n"); |
|
m_core->eval(); |
m_core->i_clk_200mhz = 0; |
m_core->eval(); |
|
m_tickcount++; |
|
/* |
if ((m_core->o_wb_ack)&&(!m_core->i_wb_cyc)) { |
printf("SETTING ERR TO TRUE!!!!! ACK w/ no CYC\n"); |
// m_bomb = true; |
} |
*/ |
} |
|
void wb_tick(void) { |
printf("WB-TICK()\n"); |
m_core->i_wb_cyc = 0; |
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
tick(); |
} |
|
unsigned wb_read(unsigned a) { |
int errcount = 0; |
unsigned result; |
|
printf("WB-READ(%08x)\n", a); |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
m_core->i_wb_we = 0; |
m_core->i_wb_addr= a & 0x03fffff; |
|
if (m_core->o_wb_stall) |
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
tick(); |
else |
tick(); |
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
while((errcount++ < BOMBCOUNT)&&(!m_core->o_wb_ack)) |
tick(); |
|
|
result = m_core->o_wb_data; |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("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"); |
m_bomb = true; |
} |
tick(); |
|
return result; |
} |
|
void wb_read(unsigned a, int len, unsigned *buf) { |
int errcount = 0; |
int THISBOMBCOUNT = BOMBCOUNT * len; |
int cnt, rdidx, inc; |
|
printf("WB-READ(%08x, %d)\n", a, len); |
|
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
wb_tick(); |
|
if (errcount >= BOMBCOUNT) { |
m_bomb = true; |
return; |
} |
|
errcount = 0; |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
m_core->i_wb_we = 0; |
m_core->i_wb_addr= a & 0x03fffff; |
|
rdidx =0; cnt = 0; |
inc = (m_core->i_wb_data_stb); |
|
do { |
int s; |
s = (m_core->o_wb_stall==0)?0:1; |
tick(); |
if (!s) |
m_core->i_wb_addr += inc; |
cnt += (s==0)?1:0; |
if (m_core->o_wb_ack) |
buf[rdidx++] = m_core->o_wb_data; |
} while((cnt < len)&&(errcount++ < THISBOMBCOUNT)); |
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
while((rdidx < len)&&(errcount++ < THISBOMBCOUNT)) { |
tick(); |
if (m_core->o_wb_ack) |
buf[rdidx++] = m_core->o_wb_data; |
} |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
|
if(errcount >= THISBOMBCOUNT) { |
printf("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"); |
m_bomb = true; |
} |
tick(); |
} |
|
void wb_write(unsigned a, unsigned int v) { |
int errcount = 0; |
|
printf("WB-WRITE(%08x) = %08x\n", a, v); |
m_core->i_wb_cyc = 1; |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
m_core->i_wb_we = 1; |
m_core->i_wb_addr= a & 0x03fffff; |
m_core->i_wb_data= v; |
|
if (m_core->o_wb_stall) |
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
tick(); |
tick(); |
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
while((errcount++ < BOMBCOUNT)&&(!m_core->o_wb_ack)) |
tick(); |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} tick(); |
} |
|
void wb_write(unsigned a, unsigned int ln, unsigned int *buf) { |
unsigned errcount = 0, nacks = 0; |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
for(unsigned stbcnt=0; stbcnt<ln; stbcnt++) { |
m_core->i_wb_we = 1; |
m_core->i_wb_addr= (a+stbcnt) & 0x03fffff; |
m_core->i_wb_data= buf[stbcnt]; |
errcount = 0; |
|
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) { |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
// Tick, now that we're not stalled. This is the tick |
// that gets accepted. |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
errcount = 0; |
while((nacks < ln)&&(errcount++ < BOMBCOUNT)) { |
tick(); |
if (m_core->o_wb_ack) { |
nacks++; |
errcount = 0; |
} |
} |
|
// Release the bus |
m_core->i_wb_cyc = 0; |
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} tick(); |
} |
|
void wb_write_slow(unsigned a, unsigned int ln, unsigned int *buf, |
int slowcounts) { |
unsigned errcount = 0, nacks = 0; |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
for(unsigned stbcnt=0; stbcnt<ln; stbcnt++) { |
m_core->i_wb_we = 1; |
m_core->i_wb_addr= (a+stbcnt) & 0x03fffff; |
m_core->i_wb_data= buf[stbcnt]; |
errcount = 0; |
|
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) { |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
|
// Tick, now that we're not stalled. This is the tick |
// that gets accepted. |
tick(); if (m_core->o_wb_ack) nacks++; |
|
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
for(int j=0; j<slowcounts; j++) { |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
|
// Turn our strobe signal back on again, after we just |
// turned it off. |
m_core->i_wb_data_stb = (a & QSPIFLASH)?1:0; |
m_core->i_wb_ctrl_stb = !(m_core->i_wb_data_stb); |
} |
|
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
errcount = 0; |
while((nacks < ln)&&(errcount++ < BOMBCOUNT)) { |
tick(); |
if (m_core->o_wb_ack) { |
nacks++; |
errcount = 0; |
} |
} |
|
// Release the bus |
m_core->i_wb_cyc = 0; |
m_core->i_wb_data_stb = 0; |
m_core->i_wb_ctrl_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} tick(); |
} |
|
bool bombed(void) const { return m_bomb; } |
|
}; |
|
int main(int argc, char **argv) { |
Verilated::commandArgs(argc, argv); |
EQSPIFLASH_TB *tb = new EQSPIFLASH_TB; |
const char *fname = "/dev/urandom"; |
unsigned rdv; |
unsigned *rdbuf; |
int idx = 4; |
|
tb->load(fname); |
rdbuf = new unsigned[4096]; |
tb->setflash(0,0); |
|
tb->wb_tick(); |
rdv = tb->wb_read(QSPIFLASH); |
printf("READ[0] = %04x\n", rdv); |
if (rdv != 0) |
goto test_failure; |
|
tb->wb_tick(); |
if (tb->bombed()) |
goto test_failure; |
|
for(int i=0; (i<1000)&&(!tb->bombed()); i++) { |
unsigned tblv; |
tblv = (*tb)[i]; |
rdv = tb->wb_read(QSPIFLASH+i); |
|
if(tblv != rdv) { |
printf("BOMB: READ[%08x] %08x, EXPECTED %08x\n", QSPIFLASH+i, 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(QSPIFLASH+1000, 1000, rdbuf); |
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<1000; i++) { |
if ((*tb)[i+1000] != rdbuf[i]) { |
printf("BOMB: V-READ[%08x] %08x, EXPECTED %08x\n", QSPIFLASH+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("EWCTRL-REG = %02x\n", rdv=tb->wb_read(0)); |
if(tb->bombed()) goto test_failure; |
printf("STATUS-REG = %02x\n", rdv=tb->wb_read(1)); |
if ((rdv != 0x1c)||(tb->bombed())) goto test_failure; |
printf("NVCONF-REG = %02x\n", tb->wb_read(2)); if(tb->bombed()) goto test_failure; |
printf("VCONFG-REG = %02x\n", tb->wb_read(3)); if(tb->bombed()) goto test_failure; |
printf("EVCONF-REG = %02x\n", tb->wb_read(4)); if(tb->bombed()) goto test_failure; |
printf("LOCK -REG = %02x\n", tb->wb_read(5)); if(tb->bombed()) goto test_failure; |
printf("FLAG -REG = %02x\n", tb->wb_read(6)); if(tb->bombed()) goto test_failure; |
|
if (tb->bombed()) |
goto test_failure; |
|
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; |
|
for(int i=0; i<16; i++) |
printf("OTP[%2d]-R = %02x\n", i, tb->wb_read(16+i)); |
if (tb->bombed()) |
goto test_failure; |
printf("OTP[CT]-R = %02x\n", tb->wb_read(15)>>24); |
|
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]; |
rdv = tb->wb_read(QSPIFLASH+i); |
|
if(tblv != rdv) { |
printf("BOMB: READ %08x, EXPECTED %08x\n", rdv, tblv); |
goto test_failure; |
break; |
} else printf("MATCH: %08x == %08x\n", rdv, tblv); |
} tb->wb_read(QSPIFLASH+1000, 1000, rdbuf); |
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<1000; i++) { |
if ((*tb)[i+1000] != rdbuf[i]) { |
printf("BOMB: READ %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]; |
rdv = tb->wb_read(QSPIFLASH+i); |
|
if(tblv != rdv) { |
printf("BOMB: READ %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(QSPIFLASH+1000, 1000, rdbuf); |
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<1000; i++) { |
if ((*tb)[i+1000] != rdbuf[i]) { |
printf("BOMB: READ %08x, EXPECTED %08x\n", rdv, (*tb)[i+1000]); |
goto test_failure; |
} |
} printf("VECTOR TEST PASSES! (QUAD+XIP)\n"); |
|
rdbuf[0] = tb->wb_read(QSPIFLASH+1023); |
rdbuf[1] = tb->wb_read(QSPIFLASH+2048); |
|
printf("Turning off write-protect, calling WEL\n"); |
tb->wb_write(0, 0x620001be); |
printf("Attempting to erase subsector 1\n"); |
tb->wb_write(0, 0xf20005be); |
|
while (tb->wb_read(0)&0x01000000) |
; |
while(tb->wb_read(0)&0x80000000) |
; |
if (tb->wb_read(QSPIFLASH+1023) != rdbuf[0]) |
goto test_failure; |
if (tb->wb_read(QSPIFLASH+2048) != rdbuf[1]) |
goto test_failure; |
tb->wb_read(QSPIFLASH+1024, 1024, rdbuf); |
for(int i=0; i<1024; i++) { |
if (rdbuf[i] != 0xffffffff) { |
printf("BOMB: SUBSECTOR ERASE, EXPECTED[0x%02x] = 0xffffffff != %08x\n", i, rdv); |
goto test_failure; |
} break; |
} |
|
// Try to execute a single write |
// Check that this will work ... |
for(idx=4; idx<4096; idx++) { |
if (0 != (tb->wb_read(QSPIFLASH+idx)&(~0x11111111))) |
break; |
} |
// First, turn the write-enable back on |
tb->wb_write(0, 0x620001be); |
// Now, write the value |
tb->wb_write(QSPIFLASH+idx, 0x11111111); |
while (tb->wb_read(0)&0x01000000) |
; |
while(tb->wb_read(0)&(0x80000000)) |
; |
if (0 != (tb->wb_read(QSPIFLASH+idx)&(~0x11111111))) |
goto test_failure; |
|
// Try to write a complete block |
{ |
FILE *fp = fopen(fname, "r"); // Open /dev/urandom |
if (4096 != fread(rdbuf, sizeof(unsigned), 4096, fp)) { |
perror("Couldnt read /dev/urandom into buffer!"); |
goto test_failure; |
} fclose(fp); |
} |
|
printf("Attempting to write subsector 1\n"); |
for(int i=0; i<1024; i+= 64) { |
printf("Turning off write-protect, calling WEL\n"); |
tb->wb_write(0, 0x620001be); |
|
printf("Writing from %08x to %08x from rdbuf\n", |
QSPIFLASH+1024+i, QSPIFLASH+1024+i+63); |
// tb->wb_write(QSPIFLASH+1024+i, 64, &rdbuf[i]); |
tb->wb_write_slow(QSPIFLASH+1024+i, 64, &rdbuf[i], 32); |
while(tb->wb_read(0)&(0x80000000)) |
; |
} |
|
tb->wb_read(QSPIFLASH+1024, 1024, &rdbuf[1024]); |
for(int i=0; i<1024; i++) { |
if (rdbuf[i] != rdbuf[i+1024]) { |
printf("BOMB: SUBSECTOR PROGRAM, EXPECTED[0x%02x] = 0x%08x != %08x\n", i, rdbuf[i], rdbuf[i+1024]); |
goto test_failure; |
} |
} |
|
// -Try to write an OTP register |
printf("Turning off write-protect, calling WEL\n"); |
tb->wb_write( 0, 0x620001be); |
printf("Writing OTP[2]\n"); |
tb->wb_write(18, 0x620001be); |
while (tb->wb_read(0)&0x01000000) |
; |
while(tb->wb_read(0)&(0x80000000)) |
; |
if (0x620001be != tb->wb_read(18)) |
goto test_failure; |
|
// -Try to write protect all OTP register |
printf("Turning off write-protect, calling WEL\n"); |
tb->wb_write( 0, 0x620001be); |
printf("Writing OTP[END]\n"); |
tb->wb_write(15, 0); |
while (tb->wb_read(0)&0x01000000) |
; |
while(tb->wb_read(0)&(0x80000000)) |
; |
if (0 != tb->wb_read(15)) |
goto test_failure; |
|
// -Try to write OTP after write protecting all OTP registers |
printf("Turning off write-protect, calling WEL\n"); |
tb->wb_write( 0, 0x620001be); |
printf("Writing OTP[7]\n"); |
tb->wb_write(16+7, 0); |
while (tb->wb_read(0)&0x01000000) |
; |
while(tb->wb_read(0)&(0x80000000)) |
; |
|
// -Verify OTP not written |
if (0 == tb->wb_read(16+7)) |
goto test_failure; |
|
|
printf("SUCCESS!!\n"); |
exit(0); |
test_failure: |
printf("FAIL-HERE\n"); |
for(int i=0; i<64; i++) |
tb->tick(); |
printf("TEST FAILED\n"); |
exit(-1); |
} |
/bench/cpp/eqspiflashsim.cpp
0,0 → 1,728
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: eqspiflashsim.cpp |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This library simulates the operation of a Quad-SPI commanded |
// flash, such as the Micron N25Q128A used on the Arty development |
// board by Digilent. As such, it is defined by 16 MBytes of |
// memory (4 MWord). |
// |
// This simulator is useful for testing in a Verilator/C++ |
// environment, where this simulator can be used in place of |
// the actual hardware. |
// |
// 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 <stdio.h> |
#include <string.h> |
#include <assert.h> |
#include <stdlib.h> |
|
#include "eqspiflashsim.h" |
|
#define MEMBYTES (1<<24) |
|
static const unsigned |
DEVESD = 0x014, |
// MICROSECONDS = 200, |
// MILLISECONDS = MICROSECONDS * 1000, |
// SECONDS = MILLISECONDS * 1000, |
MICROSECONDS = 20, |
MILLISECONDS = MICROSECONDS * 10, |
SECONDS = MILLISECONDS * 10, |
tSHSL1 = 4, // S# deselect time after a read command |
tSHSL2 = 10, // S# deselect time after a non-read command |
tW = 1300 * MICROSECONDS, // write config cycle time |
tWNVCR = 200 * MILLISECONDS, // write nonvolatile-config cycle time |
tWVECR = 8, // write volatile enhanced config cycle time |
tBE = 32 * SECONDS, // Bulk erase time |
tDP = 10 * SECONDS, // Deep power down |
tRES = 30 * SECONDS, |
// Shall we artificially speed up this process? |
// These numbers are the "typical" times |
tPP = 500 * MICROSECONDS, // Page program time |
tSE = 700 * MILLISECONDS, // Sector erase time |
tSS = 250 * MILLISECONDS; // Subsector erase time |
// These are the maximum times |
// tW = 8300 * MICROSECONDS, // write config cycle time |
// tWNVCR = 3000 * MILLISECONDS, // write nonvolatile-config cycle time |
// tWVECR = 8, // write volatile enhanced config cycle time |
// tPP = 5000 * MICROSECONDS, |
// tSE = 3000 * MILLISECONDS; |
// tSS = 800 * MILLISECONDS; |
|
static const char IDSTR[20]= { |
0x20, // Micron's ID, assigned by JEDEC |
(char)0xba, (char)0x18, // Memory type and capacity |
(char)0x10, // Length of data to follow |
(char)0xfe, (char)0xfd, // Extended device ID and device config info |
(char)0xfc, (char)0xfb, (char)0xfa, (char)0xf9, |
(char)0xf8, (char)0xf7, (char)0xf6, (char)0xf5, |
(char)0xf4, (char)0xf3, (char)0xf2, (char)0xf1, |
(char)0xf0, (char)0xef |
}; |
|
EQSPIFLASHSIM::EQSPIFLASHSIM(void) { |
const int NSECTORS = MEMBYTES>>16; |
m_mem = new char[MEMBYTES]; |
m_pmem = new char[256]; |
m_otp = new char[65]; |
for(int i=0; i<65; i++) |
m_otp[i] = 0x0ff; |
m_otp[64] = 1; |
m_otp_wp = false; |
m_lockregs = new char[NSECTORS]; |
for(int i=0; i<NSECTORS; i++) |
m_lockregs[i] = 0; |
|
m_state = EQSPIF_IDLE; |
m_last_sck = 1; |
m_write_count = 0; |
m_ireg = m_oreg = 0; |
m_sreg = 0x01c; |
m_creg = 0x001; // Initial creg on delivery |
m_config = 0x7; // Volatile configuration register |
m_nvconfig = 0x0fff; // Nonvolatile configuration register |
m_quad_mode = false; |
m_mode_byte = 0; |
m_flagreg = 0x0a5; |
|
m_debug = true; |
|
memset(m_mem, 0x0ff, MEMBYTES); |
} |
|
void EQSPIFLASHSIM::load(const unsigned addr, const char *fname) { |
FILE *fp; |
size_t len; |
|
if (addr >= MEMBYTES) |
return; // return void |
len = 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); |
perror("O/S Err:"); |
} |
} else { |
fprintf(stderr, "SPI-FLASH: Could not open %s\n", fname); |
perror("O/S Err:"); |
} |
} |
|
#define QOREG(A) m_oreg = ((m_oreg & (~0x0ff))|(A&0x0ff)) |
|
int EQSPIFLASHSIM::operator()(const int csn, const int sck, const int dat) { |
// Keep track of a timer to determine when page program and erase |
// cycles complete. |
|
if (m_write_count > 0) { |
if (0 == (--m_write_count)) {// When done with erase/page pgm, |
// Clear the write in progress bit, together with the |
// write enable bit. |
m_sreg &= 0x0fc; |
if (m_debug) printf("Write complete, clearing WIP (inside SIM)\n"); |
} |
} |
|
if (csn) { |
m_last_sck = 1; |
m_ireg = 0; m_oreg = 0; |
|
if ((EQSPIF_PP == m_state)||(EQSPIF_QPP == m_state)) { |
// Start a page program |
if (m_debug) printf("EQSPI: Page Program write cycle begins\n"); |
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07); |
if (m_debug) printf("EQSPI: pmem = %08lx\n", (unsigned long)m_pmem); |
assert((m_lockregs[(m_addr>>16)&0x0ff]&0x1)==0); |
assert((m_count & 7)==0); |
m_write_count = tPP; |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
m_sreg |= (EQSPIF_WIP_FLAG); |
for(int i=0; i<256; i++) { |
/* |
if (m_debug) printf("%02x: m_mem[%02x] = %02x &= %02x = %02x\n", |
i, (m_addr&(~0x0ff))+i, |
m_mem[(m_addr&(~0x0ff))+i]&0x0ff, m_pmem[i]&0x0ff, |
m_mem[(m_addr&(~0x0ff))+i]& m_pmem[i]&0x0ff); |
*/ |
m_mem[(m_addr&(~0x0ff))+i] &= m_pmem[i]; |
} |
m_quad_mode = false; |
} else if (EQSPIF_WRCR == m_state) { |
if (m_debug) printf("Actually writing volatile config register\n"); |
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"); |
m_write_count = tWNVCR; |
m_state = EQSPIF_IDLE; |
} else if (EQSPIF_WREVCONFIG == m_state) { |
if (m_debug) printf("Actually writing Enhanced volatile config register\n"); |
m_state = EQSPIF_IDLE; |
} else if (EQSPIF_WRSR == m_state) { |
if (m_debug) printf("Actually writing status register\n"); |
m_write_count = tW; |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
m_sreg |= (EQSPIF_WIP_FLAG); |
} else if (EQSPIF_WRLOCK == m_state) { |
if (m_debug) printf("Actually writing lock register\n"); |
m_write_count = tW; |
m_state = EQSPIF_IDLE; |
} else if (EQSPIF_CLRFLAGS == m_state) { |
if (m_debug) printf("Actually clearing the flags register bits\n"); |
m_state = EQSPIF_IDLE; |
m_flagreg &= 0x09f; |
} else if (m_state == EQSPIF_SUBSECTOR_ERASE) { |
if (m_debug) printf("Actually Erasing subsector, from %08x\n", m_addr); |
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07); |
assert(((m_count & 7)==0)&&(m_count == 32)); |
assert((m_lockregs[(m_addr>>16)&0x0ff]&0x1)==0); |
m_write_count = tSS; |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
m_sreg |= (EQSPIF_WIP_FLAG); |
m_addr &= (-1<<12); |
for(int i=0; i<(1<<12); i++) |
m_mem[m_addr + i] = 0x0ff; |
if (m_debug) printf("Now waiting %d ticks delay\n", m_write_count); |
} else if (m_state == EQSPIF_SECTOR_ERASE) { |
if (m_debug) printf("Actually Erasing sector, from %08x\n", m_addr); |
m_write_count = tSE; |
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07); |
assert(((m_count & 7)==0)&&(m_count == 32)); |
assert((m_lockregs[(m_addr>>16)&0x0ff]&0x1)==0); |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
m_sreg |= (EQSPIF_WIP_FLAG); |
m_addr &= (-1<<16); |
for(int i=0; i<(1<<16); i++) |
m_mem[m_addr + i] = 0x0ff; |
if (m_debug) printf("Now waiting %d ticks delay\n", m_write_count); |
} else if (m_state == EQSPIF_BULK_ERASE) { |
m_write_count = tBE; |
m_state = EQSPIF_IDLE; |
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++) |
m_mem[i] = 0x0ff; |
} else if (m_state == EQSPIF_PROGRAM_OTP) { |
// Program the One-Time Programmable (OTP memory |
if (m_debug) printf("EQSPI: OTP Program write cycle begins\n"); |
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07); |
// assert((m_lockregs[(m_addr>>16)&0x0ff]&0x1)==0); |
assert((m_count & 7)==0); |
m_write_count = tPP; // OTP cycle time as well |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
m_sreg |= (EQSPIF_WIP_FLAG); |
for(int i=0; i<65; i++) |
m_otp[i] &= m_pmem[i]; |
m_otp_wp = ((m_otp[64]&1)==0); |
/* |
} else if (m_state == EQSPIF_DEEP_POWER_DOWN) { |
m_write_count = tDP; |
m_state = EQSPIF_IDLE; |
} else if (m_state == EQSPIF_RELEASE) { |
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_state = EQSPIF_XIP; |
// } else if (m_state == EQSPIF_XIP) { |
} |
|
m_oreg = 0x0fe; |
m_count= 0; |
int out = m_nxtout[3]; |
m_nxtout[3] = m_nxtout[2]; |
m_nxtout[2] = m_nxtout[1]; |
m_nxtout[1] = m_nxtout[0]; |
m_nxtout[0] = dat; |
return out; |
} else if ((!m_last_sck)||(sck == m_last_sck)) { |
// Only change on the falling clock edge |
// printf("SFLASH-SKIP, CLK=%d -> %d\n", m_last_sck, sck); |
m_last_sck = sck; |
int out = m_nxtout[3]; |
m_nxtout[3] = m_nxtout[2]; |
m_nxtout[2] = m_nxtout[1]; |
m_nxtout[1] = m_nxtout[0]; |
if (m_quad_mode) |
m_nxtout[0] = (m_oreg>>8)&0x0f; |
else |
// return ((m_oreg & 0x0100)?2:0) | (dat & 0x0d); |
m_nxtout[0] = (m_oreg & 0x0100)?2:0; |
return out; |
} |
|
// We'll only get here if ... |
// last_sck = 1, and sck = 0, thus transitioning on the |
// negative edge as with everything else in this interface |
if (m_quad_mode) { |
m_ireg = (m_ireg << 4) | (dat & 0x0f); |
m_count+=4; |
m_oreg <<= 4; |
} else { |
m_ireg = (m_ireg << 1) | (dat & 1); |
m_count++; |
m_oreg <<= 1; |
} |
|
|
// printf("PROCESS, COUNT = %d, IREG = %02x\n", m_count, m_ireg); |
if (m_state == EQSPIF_XIP) { |
assert(m_quad_mode); |
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_state = EQSPIF_QUAD_READ; |
} 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) { |
case 0x01: // Write status register |
if (2 !=(m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: WEL not set, cannot write status reg\n"); |
m_state = EQSPIF_INVALID; |
} else |
m_state = EQSPIF_WRSR; |
break; |
case 0x02: // Normal speed (normal SPI, 1wire MOSI) Page program |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: Cannot program at this time, SREG = %x\n", m_sreg); |
m_state = EQSPIF_INVALID; |
} else { |
m_state = EQSPIF_PP; |
if (m_debug) printf("PAGE-PROGRAM COMMAND ACCEPTED\n"); |
} |
break; |
case 0x03: // Read data bytes |
// Our clock won't support this command, so go |
// to an invalid state |
if (m_debug) printf("EQSPI INVALID: This sim does not support slow reading\n"); |
m_state = EQSPIF_INVALID; |
break; |
case 0x04: // Write disable |
m_state = EQSPIF_IDLE; |
m_sreg &= (~EQSPIF_WEL_FLAG); |
break; |
case 0x05: // Read status register |
m_state = EQSPIF_RDSR; |
if (m_debug) printf("EQSPI: READING STATUS REGISTER: %02x\n", m_sreg); |
QOREG(m_sreg); |
break; |
case 0x06: // Write enable |
m_state = EQSPIF_IDLE; |
m_sreg |= EQSPIF_WEL_FLAG; |
if (m_debug) printf("EQSPI: WRITE-ENABLE COMMAND ACCEPTED\n"); |
break; |
case 0x0b: // Here's the read that we support |
if (m_debug) printf("EQSPI: FAST-READ (single-bit)\n"); |
m_state = EQSPIF_FAST_READ; |
break; |
case 0x20: // Subsector Erase |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: WEL not set, cannot do a subsector erase\n"); |
m_state = EQSPIF_INVALID; |
assert(0&&"WEL not set"); |
} else |
m_state = EQSPIF_SUBSECTOR_ERASE; |
break; |
case 0x32: // QUAD Page program, 4 bits at a time |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: Cannot program at this time, SREG = %x\n", m_sreg); |
m_state = EQSPIF_INVALID; |
assert(0&&"WEL not set"); |
} else { |
m_state = EQSPIF_QPP; |
if (m_debug) printf("EQSPI: QUAD-PAGE-PROGRAM COMMAND ACCEPTED\n"); |
if (m_debug) printf("EQSPI: pmem = %08lx\n", (unsigned long)m_pmem); |
} |
break; |
case 0x42: // Program OTP array |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: WEL not set, cannot program OTP\n"); |
m_state = EQSPIF_INVALID; |
} else if (m_otp_wp) { |
if (m_debug) printf("EQSPI: OTP Write protect is set, cannot program OTP ever again\n"); |
m_state = EQSPIF_INVALID; |
} else |
m_state = EQSPIF_PROGRAM_OTP; |
break; |
case 0x4b: // Read OTP array |
m_state = EQSPIF_READ_OTP; |
QOREG(0); |
if (m_debug) printf("EQSPI: Read OTP array command\n"); |
break; |
case 0x50: // Clear flag status register |
m_state = EQSPIF_CLRFLAGS; |
if (m_debug) printf("EQSPI: Clearing FLAGSTATUS REGISTER: %02x\n", m_flagreg); |
QOREG(m_flagreg); |
break; |
case 0x61: // WRITE Enhanced volatile config register |
m_state = EQSPIF_WREVCONFIG; |
if (m_debug) printf("EQSPI: WRITING EVCONFIG REGISTER\n"); |
break; |
case 0x65: // Read Enhanced volatile config register |
m_state = EQSPIF_RDEVCONFIG; |
if (m_debug) printf("EQSPI: READING EVCONFIG REGISTER: %02x\n", m_evconfig); |
QOREG(m_evconfig); |
break; |
case 0x06b: |
m_state = EQSPIF_QUAD_READ_CMD; |
// m_quad_mode = true; // Not yet, need to wait past dummy registers |
break; |
case 0x70: // Read flag status register |
m_state = EQSPIF_RDFLAGS; |
if (m_debug) printf("EQSPI: READING FLAGSTATUS REGISTER: %02x\n", m_flagreg); |
QOREG(m_flagreg); |
break; |
case 0x81: // Write volatile config register |
m_state = EQSPIF_WRCR; |
if (m_debug) printf("EQSPI: WRITING CONFIG REGISTER: %02x\n", m_config); |
break; |
case 0x85: // Read volatile config register |
m_state = EQSPIF_RDCR; |
if (m_debug) printf("EQSPI: READING CONFIG REGISTER: %02x\n", m_config); |
QOREG(m_config>>8); |
break; |
case 0x9e: // Read ID (fall through) |
case 0x9f: // Read ID |
m_state = EQSPIF_RDID; m_addr = 0; |
if (m_debug) printf("EQSPI: READING ID\n"); |
QOREG(IDSTR[0]); |
break; |
case 0xb1: // Write nonvolatile config register |
m_state = EQSPIF_WRNVCONFIG; |
if (m_debug) printf("EQSPI: WRITING NVCONFIG REGISTER: %02x\n", m_nvconfig); |
break; |
case 0xb5: // Read nonvolatile config register |
m_state = EQSPIF_RDNVCONFIG; |
if (m_debug) printf("EQSPI: READING NVCONFIG REGISTER: %02x\n", m_nvconfig); |
QOREG(m_nvconfig>>8); |
break; |
case 0xc7: // Bulk Erase |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: WEL not set, cannot erase device\n"); |
m_state = EQSPIF_INVALID; |
} else |
m_state = EQSPIF_BULK_ERASE; |
break; |
case 0xd8: // Sector Erase |
if (2 != (m_sreg & 0x203)) { |
if (m_debug) printf("EQSPI: WEL not set, cannot erase sector\n"); |
m_state = EQSPIF_INVALID; |
assert(0&&"WEL not set"); |
} else { |
m_state = EQSPIF_SECTOR_ERASE; |
if (m_debug) printf("EQSPI: SECTOR_ERASE COMMAND\n"); |
} |
break; |
case 0xe5: // Write lock register |
m_state = EQSPIF_WRLOCK; |
if (m_debug) printf("EQSPI: WRITING LOCK REGISTER\n"); |
break; |
case 0xe8: // Read lock register |
m_state = EQSPIF_RDLOCK; |
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"); |
break; |
default: |
printf("EQSPI: UNRECOGNIZED SPI FLASH CMD: %02x\n", m_ireg&0x0ff); |
m_state = EQSPIF_INVALID; |
assert(0 && "Unrecognized command\n"); |
break; |
} |
} else if ((0 == (m_count&0x07))&&(m_count != 0)) { |
QOREG(0); |
switch(m_state) { |
case EQSPIF_IDLE: |
printf("TOO MANY CLOCKS, SPIF in IDLE\n"); |
break; |
case EQSPIF_WRSR: |
if (m_count == 16) { |
m_sreg = (m_sreg & 0x07c) | (m_ireg & 0x07c); |
if (m_debug) printf("Request to set sreg to 0x%02x\n", |
m_ireg&0x0ff); |
} else { |
printf("TOO MANY CLOCKS FOR WRR!!!\n"); |
exit(-2); |
m_state = EQSPIF_IDLE; |
} |
break; |
case EQSPIF_WRCR: // Write volatile config register, 0x81 |
if (m_count == 8) { |
m_config = m_ireg & 0x0ff; |
printf("Setting volatile config register to %08x\n", m_config); |
assert((m_config & 0xfb)==0x8b); |
} break; |
case EQSPIF_WRNVCONFIG: // Write nonvolatile config register |
if (m_count == 8) { |
m_nvconfig = m_ireg & 0x0ffdf; |
printf("Setting nonvolatile config register to %08x\n", m_config); |
assert((m_nvconfig & 0xffc5)==0x8fc5); |
} break; |
case EQSPIF_WREVCONFIG: // Write enhanced volatile config reg |
if (m_count == 8) { |
m_evconfig = m_ireg & 0x0ff; |
printf("Setting enhanced volatile config register to %08x\n", m_evconfig); |
assert((m_evconfig & 0x0d7)==0xd7); |
} break; |
case EQSPIF_WRLOCK: |
if (m_count == 32) { |
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); |
} 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]); |
} else |
QOREG(m_lockregs[m_addr]); |
break; |
case EQSPIF_CLRFLAGS: |
assert(0 && "Too many clocks for CLSR command!!\n"); |
break; |
case EQSPIF_READ_OTP: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0ffffff; |
assert(m_addr < 65); |
m_otp[64] = (m_otp_wp)?0:1; |
|
if (m_debug) printf("READOTP, SETTING ADDR = %08x (%02x:%02x:%02x:%02x)\n", m_addr, |
((m_addr<65)?m_otp[m_addr]:0)&0x0ff, |
((m_addr<64)?m_otp[m_addr+1]:0)&0x0ff, |
((m_addr<63)?m_otp[m_addr+2]:0)&0x0ff, |
((m_addr<62)?m_otp[m_addr+3]:0)&0x0ff); |
if (m_debug) printf("READOTP, Array is %s, m_otp[64] = %d\n", |
(m_otp_wp)?"Locked":"Unlocked", |
m_otp[64]); |
QOREG(m_otp[m_addr]); |
} else if (m_count < 40) { |
} // else if (m_count == 40) |
else if ((m_count&7)==0) { |
if (m_debug) printf("READOTP, ADDR = %08x\n", m_addr); |
if (m_addr < 65) |
QOREG(m_otp[m_addr]); |
else |
QOREG(0); |
if (m_debug) printf("EQSPI: READING OTP, %02x%s\n", |
(m_addr<65)?m_otp[m_addr]&0x0ff:0xfff, |
(m_addr > 65)?"-- PAST OTP LENGTH!":""); |
m_addr++; |
} |
break; |
case EQSPIF_RDID: |
if ((m_count&7)==0) { |
m_addr++; |
if (m_debug) printf("READID, ADDR = %08x\n", m_addr); |
if (m_addr < sizeof(IDSTR)) |
QOREG(IDSTR[m_addr]); |
else |
QOREG(0); |
if (m_debug) printf("EQSPI: READING ID, %02x%s\n", |
IDSTR[m_addr]&0x0ff, |
(m_addr >= sizeof(IDSTR))?"-- PAST ID LENGTH!":""); |
} |
break; |
case EQSPIF_RDSR: |
// printf("Read SREG = %02x, wait = %08x\n", m_sreg, |
// m_write_count); |
QOREG(m_sreg); |
break; |
case EQSPIF_RDCR: |
if (m_debug) printf("Read CREG = %02x\n", m_creg); |
QOREG(m_creg); |
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; |
if (m_debug) printf("FAST READ, ADDR = %08x\n", m_addr); |
QOREG(0x0c3); |
assert((m_addr & 0xf000003)==0); |
} else if ((m_count >= 40)&&(0 == (m_sreg&0x01))) { |
if (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); |
} else if (0 != (m_sreg&0x01)) { |
m_oreg = 0; |
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 |
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<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) { |
QOREG(m_mem[m_addr++]); |
m_quad_mode = true; |
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); |
} else { |
printf("ERR: EQSPIF--TRYING TO READ WHILE BUSY! (count = %d)\n", m_count); |
m_oreg = 0; |
} |
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); |
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); |
} else { |
m_oreg = 0; |
printf("EQSPI/QR ... m_count = %d\n", m_count); |
} |
break; |
case EQSPIF_PP: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0ffffff; |
if (m_debug) printf("EQSPI: PAGE-PROGRAM ADDR = %06x\n", m_addr); |
assert((m_addr & 0xfc00000)==0); |
// m_page = m_addr >> 8; |
for(int i=0; i<256; i++) |
m_pmem[i] = 0x0ff; |
} else if (m_count >= 40) { |
m_pmem[m_addr & 0x0ff] = m_ireg & 0x0ff; |
// printf("EQSPI: 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_QPP: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0ffffff; |
m_quad_mode = true; |
if (m_debug) printf("EQSPI/QR: PAGE-PROGRAM ADDR = %06x\n", m_addr); |
assert((m_addr & 0xfc00000)==0); |
// m_page = m_addr >> 8; |
for(int i=0; i<256; i++) |
m_pmem[i] = 0x0ff; |
} else if (m_count >= 40) { |
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: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0fff000; |
if (m_debug) printf("SUBSECTOR_ERASE ADDRESS = %08x\n", m_addr); |
assert((m_addr & 0xff000000)==0); |
} break; |
case EQSPIF_SECTOR_ERASE: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0ff0000; |
if (m_debug) printf("SECTOR_ERASE ADDRESS = %08x\n", m_addr); |
assert((m_addr & 0xf000000)==0); |
} break; |
case EQSPIF_PROGRAM_OTP: |
if (m_count == 32) { |
m_addr = m_ireg & 0x0ff; |
for(int i=0; i<65; i++) |
m_pmem[i] = 0x0ff; |
} else if ((m_count >= 40)&&(m_addr < 65)) { |
m_pmem[m_addr++] = m_ireg & 0x0ff; |
} break; |
/* |
case EQSPIF_RELEASE: |
if (m_count >= 32) { |
QOREG(DEVESD); |
} break; |
*/ |
default: |
printf("EQSPI ... DEFAULT OP???\n"); |
QOREG(0xff); |
break; |
} |
} // else printf("SFLASH->count = %d\n", m_count); |
|
m_last_sck = sck; |
int out = m_nxtout[3]; |
m_nxtout[3] = m_nxtout[2]; |
m_nxtout[2] = m_nxtout[1]; |
m_nxtout[1] = m_nxtout[0]; |
if (m_quad_mode) |
m_nxtout[0] = (m_oreg>>8)&0x0f; |
else |
m_nxtout[0] = (m_oreg & 0x0100)?2:0; |
return out; |
} |
|
/bench/cpp/Makefile
0,0 → 1,71
################################################################################ |
## |
## Filename: Makefile |
## |
## Project: Wishbone Controlled Quad SPI Flash Controller |
## |
## Purpose: This makefile coordinates the build of the extended quad spi |
## flash controller (not the normal quad spi flash controller also |
## in this directory). |
## |
## 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 |
## |
## |
################################################################################ |
## |
## |
CXX := g++ |
FLAGS := -Wall -Og -g |
OBJDIR := obj-pc |
RTLD := ../../rtl |
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I/usr/share/verilator/include |
SOURCES := eqspiflashsim.cpp eqspiflash_tb.cpp |
VOBJDR := $(RTLD)/obj_dir |
VLIB := /usr/share/verilator/include/verilated.cpp |
SIMSRCS := enetctrlsim.cpp eqspiflashsim.cpp |
all: $(OBJDIR)/ eqspiflash_tb pretest |
|
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
|
$(OBJDIR)/%.o: %.cpp |
$(CXX) $(FLAGS) -c $^ -o $@ |
|
eqspiflash_tb: eqspiflash_tb.cpp $(OBJDIR)/eqspiflashsim.o $(VOBJDR)/Veqspiflash__ALL.a |
$(CXX) $(FLAGS) $(INCS) $^ $(VOBJDR)/Veqspiflash__ALL.a $(VLIB) -o $@ |
|
.PHONY: pretest |
pretest: 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 |
./eqspiflash_tb |
|
.PHONY: clean |
clean: |
rm ./eqspiflash_tb |
/bench/cpp/eqspiflashsim.h
0,0 → 1,117
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: eqspiflashsim.h |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This library simulates the operation of an Extended Quad-SPI |
// commanded flash, such as the N25Q128A used on the Arty |
// development board by Digilent. As such, it is defined by |
// 16 MBytes of memory (4 MWords). |
// |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef EQSPIFLASHSIM_H |
#define EQSPIFLASHSIM_H |
|
#define EQSPIF_WIP_FLAG 0x0001 |
#define EQSPIF_WEL_FLAG 0x0002 |
#define EQSPIF_DEEP_POWER_DOWN_FLAG 0x0200 |
class EQSPIFLASHSIM { |
typedef enum { |
EQSPIF_IDLE, |
EQSPIF_XIP, |
EQSPIF_RDSR, |
EQSPIF_RDCR, |
EQSPIF_RDNVCONFIG, |
EQSPIF_RDEVCONFIG, |
EQSPIF_WRSR, |
EQSPIF_WRCR, |
EQSPIF_WRNVCONFIG, |
EQSPIF_WREVCONFIG, |
EQSPIF_RDFLAGS, |
EQSPIF_CLRFLAGS, |
EQSPIF_RDLOCK, |
EQSPIF_WRLOCK, |
EQSPIF_RDID, |
EQSPIF_RELEASE, |
EQSPIF_FAST_READ, |
EQSPIF_QUAD_READ_CMD, |
EQSPIF_QUAD_READ, |
EQSPIF_PP, |
EQSPIF_QPP, |
// Erase states |
EQSPIF_SUBSECTOR_ERASE, |
EQSPIF_SECTOR_ERASE, |
EQSPIF_BULK_ERASE, |
// OTP memory |
EQSPIF_PROGRAM_OTP, |
EQSPIF_READ_OTP, |
// |
EQSPIF_INVALID |
} EQSPIF_STATE; |
|
EQSPIF_STATE m_state; |
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_config, m_mode_byte, m_creg, |
m_nvconfig, m_evconfig, m_flagreg, m_nxtout[4]; |
bool m_quad_mode, m_debug, m_otp_wp; |
|
public: |
EQSPIFLASHSIM(void); |
void load(const char *fname) { load(0, fname); } |
void load(const unsigned addr, const char *fname); |
void debug(const bool dbg) { m_debug = dbg; } |
bool debug(void) const { return m_debug; } |
bool write_enabled(void) const { return m_debug; } |
unsigned counts_till_idle(void) const { |
return m_write_count; } |
unsigned operator[](const int index) { |
unsigned char *cptr = (unsigned char *)&m_mem[index<<2]; |
unsigned v; |
v = (*cptr++); |
v = (v<<8)|(*cptr++); |
v = (v<<8)|(*cptr++); |
v = (v<<8)|(*cptr); |
|
return v; } |
void set(const unsigned addr, const unsigned val) { |
unsigned char *cptr = (unsigned char *)&m_mem[addr<<2]; |
*cptr++ = (val>>24); |
*cptr++ = (val>>16); |
*cptr++ = (val>> 8); |
*cptr = (val); |
return;} |
int operator()(const int csn, const int sck, const int dat); |
}; |
|
#endif |