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

Subversion Repositories qspiflash

Compare Revisions

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

Rev 9 → Rev 12

/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);
}
/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;
}
 
/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
/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

powered by: WebSVN 2.1.0

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