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

Subversion Repositories openarty

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openarty/trunk
    from Rev 57 to Rev 58
    Reverse comparison

Rev 57 → Rev 58

/sim/verilated/Makefile
0,0 → 1,131
################################################################################
##
## Filename: Makefile
##
## Project: OpenArty, an entirely open SoC based upon the Arty platform
##
## Purpose:
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
################################################################################
##
## Copyright (C) 2015-2017, Gisselquist Technology, LLC
##
## This program is free software (firmware): you can redistribute it and/or
## modify it under the terms of the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or (at
## your option) any later version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program. (It's in the $(ROOT)/doc directory. Run make with no
## target there if the PDF file isn't present.) If not, see
## <http://www.gnu.org/licenses/> for a copy.
##
## License: GPL, v3, as defined and found on www.gnu.org,
## http://www.gnu.org/licenses/gpl.html
##
##
################################################################################
##
##
CXX := g++
OBJDIR := obj-pc
RTLD := ../../rtl
VERILATOR_ROOT ?= $(shell bash -c 'verilator -V|grep VERILATOR_ROOT | head -1 | sed -e " s/^.*=\s*//"')
VROOT := $(VERILATOR_ROOT)
GFXFLAGS:= `pkg-config gtkmm-3.0 --cflags`
GFXLIBS := `pkg-config gtkmm-3.0 --cflags --libs`
FLAGS := -Wall -Og -g
VINCD := $(VROOT)/include
INCS := -I$(RTLD)/obj_dir/ -I$(RTLD) -I$(VINCD) -I$(VINCD)/vltstd
SOURCES := fastmaster_tb.cpp eqspiflashsim.cpp eqspiflash_tb.cpp \
oledsim.cpp enetctrlsim.cpp zipelf.cpp byteswap.cpp \
memsim.cpp sdspisim.cpp uartsim.cpp ddrsdramsim.cpp
HEADERS := ddrsdramsim.h enetctrlsim.h eqspiflashsim.h memsim.h \
oledsim.h pipecmdr.h port.h sdspisim.h testb.h uartsim.h zipelf.h
VOBJDR := $(RTLD)/obj_dir
VOBJS := $(OBJDIR)/verilated.o $(OBJDIR)/verilated_vcd_c.o
SIMSRCS := enetctrlsim.cpp eqspiflashsim.cpp zipelf.cpp \
memsim.cpp sdspisim.cpp uartsim.cpp oledsim.cpp byteswap.cpp
SIMOBJ := $(subst .cpp,.o,$(SIMSRCS))
SIMOBJS:= $(addprefix $(OBJDIR)/,$(SIMOBJ))
PROGRAMS := busmaster_tb eqspiflash_tb enetctrl_tb
all: $(OBJDIR)/ $(PROGRAMS)
 
$(OBJDIR)/:
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi"
 
$(OBJDIR)/%.o: %.cpp
$(CXX) $(FLAGS) $(INCS) -c $< -o $@
 
$(OBJDIR)/%.o: $(VINCD)/%.cpp
$(CXX) $(FLAGS) $(INCS) -c $< -o $@
 
.PHONY: oledsim.o
oledsim.o: $(OBJDIR)/oledsim.o
$(OBJDIR)/oledsim.o: oledsim.cpp
$(CXX) $(FLAGS) $(GFXFLAGS) -c $< -o $@
 
# While busmaster_tb.o isnt really dependent upon fastmaster_tb.o, this
# makes certain that all of the dependencies of fastmaster_tb are captured
# in busmaster. Hence, if fastmaster_tb is remade/rebuilt because one of
# its dependencies change, so too is busmaster_tb.
#
$(OBJDIR)/busmaster_tb.o: fastmaster_tb.cpp
$(CXX) $(FLAGS) $(GFXFLAGS) $(INCS) -c $< -o $@
 
$(OBJDIR)/fastmaster_tb.o: fastmaster_tb.cpp
$(CXX) -DFASTCLK $(FLAGS) $(GFXFLAGS) $(INCS) -c $< -o $@
 
 
 
eqspiflash_tb: $(OBJDIR)/eqspiflash_tb.o $(OBJDIR)/eqspiflashsim.o
eqspiflash_tb: $(VOBJS) $(VOBJDR)/Veqspiflash__ALL.a
$(CXX) $(FLAGS) $(INCS) $^ $(VOBJDR)/Veqspiflash__ALL.a -o $@
 
enetctrl_tb: enetctrl_tb.cpp $(OBJDIR)/enetctrlsim.o
enetctrl_tb: $(VOBJS) $(VOBJDR)/Venetctrl__ALL.a
$(CXX) $(FLAGS) $(INCS) $^ $(VOBJDR)/Venetctrl__ALL.a -o $@
 
fastmaster_tb: $(OBJDIR)/fastmaster_tb.o $(SIMOBJS)
fastmaster_tb: $(VOBJS) $(VOBJDR)/Vfastmaster__ALL.a
$(CXX) $(GFXLIBS) $^ $(VOBJDR)/Vfastmaster__ALL.a -lelf -o $@
busmaster_tb: $(OBJDIR)/busmaster_tb.o $(SIMOBJS)
busmaster_tb: $(VOBJS) $(VOBJDR)/Vbusmaster__ALL.a
$(CXX) $(GFXLIBS) $(INCS) $^ $(VOBJDR)/Vbusmaster__ALL.a $(GFXLIBS) -lelf -o $@
 
define build-depends
@echo "Building dependency file(s)"
@$(CXX) $(GFXFLAGS) $(INCS) -MM $(SOURCES) > $(OBJDIR)/xdepends.txt
@sed -e 's/^.*.o: /$(OBJDIR)\/&/' < $(OBJDIR)/xdepends.txt \
| sed -e 's/fastmaster_tb.o/busmaster_tb.o/g' \
> $(OBJDIR)/depends.txt
@rm $(OBJDIR)/xdepends.txt
endef
 
tags: $(SOURCES) $(HEADERS)
@echo "Generating tags"
@ctags $(SOURCES) $(HEADERS)
 
.PHONY: clean
clean:
rm -f *.vcd
rm -rf $(OBJDIR)/
rm -f $(PROGRAMS)
# rm -f ./fastmaster_tb
 
.PHONY: depends
depends:
$(build-depends)
 
$(OBJDIR)/depends.txt: $(OBJDIR)/ $(SOURCES) $(HEADERS)
$(build-depends)
 
-include $(OBJDIR)/depends.txt
/sim/verilated/byteswap.cpp
0,0 → 1,86
////////////////////////////////////////////////////////////////////////////////
//
// Filename: byteswap.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
// 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 <stdint.h>
#include "byteswap.h"
 
uint32_t
byteswap(uint32_t v) {
uint32_t r = 0;
 
r = (v & 0x0ff);
r <<= 8; v >>= 8;
r |= (v & 0x0ff);
r <<= 8; v >>= 8;
r |= (v & 0x0ff);
r <<= 8; v >>= 8;
r |= (v & 0x0ff);
 
return r;
}
 
uint32_t
buildword(const unsigned char *p) {
uint32_t r = 0;
 
r = (*p++); r <<= 8;
r |= (*p++); r <<= 8;
r |= (*p++); r <<= 8;
r |= (*p );
 
return r;
}
 
uint32_t
buildswap(const unsigned char *p) {
uint32_t r = 0;
 
r = p[3]; r <<= 8;
r |= p[2]; r <<= 8;
r |= p[1]; r <<= 8;
r |= p[0];
 
return r;
}
 
void
byteswapbuf(int ln, uint32_t *buf) {
for(int i=0; i<ln; i++)
buf[i] = byteswap(buf[i]);
}
 
 
/sim/verilated/byteswap.h
0,0 → 1,54
////////////////////////////////////////////////////////////////////////////////
//
// Filename: byteswap.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
// 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 BYTESWAP_H
#define BYTESWAP_H
 
#include <stdint.h>
 
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
extern uint32_t byteswap(uint32_t v);
extern void byteswapbuf(int ln, uint32_t *buf);
#else
#define byteswap(A) (A)
#define byteswapbuf(A, B)
#endif
 
extern uint32_t buildword(const unsigned char *p);
extern uint32_t buildswap(const unsigned char *p);
 
#endif
/sim/verilated/ddrsdramsim.cpp
0,0 → 1,496
////////////////////////////////////////////////////////////////////////////////
//
// Filename: ddrsdramsim.cpp
//
// Project: A wishbone controlled DDR3 SDRAM memory controller.
//
// Purpose:
//
// 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 <assert.h>
 
#define PREFIX "DDR3-SDRAM"
const unsigned ckCL = 11,
ckRP = 11,
ckRC = 10,
ckRAS = 7,
ckRFC = 320, // Clocks from refresh to activate
ckREFI = 1560, // 7.8us @ 200MHz = 7.8e-6 * 200e6 = 1560
DDR_MR2 = 0x040 | (((ckCL-5)&7)<<3),
DDR_MR1 = 0x0844,
DDR_MR0 = 0x0200 | (((ckCL-4)&0x07)<<4) | ((ckCL>11)?0x4:0);
/*
const unsigned nREF = 4,
ckREFIn = nREF*ckREFI - (nREF-1) * ckRFC;
*/
const unsigned nREF = 1,
ckREFIn = ckREFI;
 
#include "ddrsdramsim.h"
 
BANKINFO::BANKINFO(void) {
m_state = 0; m_row = 0; m_wcounter = 0; m_min_time_before_precharge=0;
}
 
void BANKINFO::tick(int cmd, unsigned addr) {
if (m_wcounter)
m_wcounter--;
switch(cmd) {
case DDR_REFRESH:
assert(m_state == 0);
break;
case DDR_PRECHARGE:
// assert((m_state&((1<<ckRP)-1)) == ((1<<ckRP)-1));
m_state &= -2;
// While the specification allows precharging an already
// precharged bank, we can keep that from happening
// here:
// assert(m_state&7);
// Only problem is, this will currently break our
// refresh logic.
/*
if (m_min_time_before_precharge != 0) {
printf("BANK-FAIL: TIME-BEFORE-PRECHARGE = %d (should be zero)\n", m_min_time_before_precharge);
assert(m_min_time_before_precharge == 0);
} if (m_min_time_before_activate != 0) {
printf("BANK-FAIL: TIME-BEFORE-ACTIVATE = %d (should be zero)\n", m_min_time_before_activate);
assert(m_min_time_before_activate==0);
}
*/
break;
case DDR_ACTIVATE:
assert((m_state&((1<<ckRP)-1)) == 0);
if (((m_state&7)!=0)&&((addr&0x7fff) != m_row)) {
printf("BANK-FAIL: Attempt to Activate an already active bank without closing it first (m_state = %x)\n", m_state);
assert((m_state&7)==0);
}
 
/*
if (m_wcounter != 0) {
printf("BANK-FAIL: ACTIVATE too soon after write (wcounter = %d)\n", m_wcounter);
assert(m_wcounter == 0);
} if (m_min_time_before_activate!=0) {
printf("BANK-FAIL: ACTIVATE too soon after last activate, (ctr=%d)\n", m_min_time_before_activate);
assert(m_min_time_before_activate==0);
}
*/
m_state = 1;
m_row = addr & 0x7fff;
m_min_time_before_precharge = ckRAS;
m_min_time_before_activate = ckRC;
break;
case DDR_READ: case DDR_WRITE:
if (DDR_READ)
assert(m_wcounter == 0);
else
m_wcounter = 3+4+4;
if ((m_state&((1<<ckRP)-1)) != ((1<<ckRP)-1)) {
printf(PREFIX "::R/W Error: m_state = %08x, ckRP = %d (%08x)\n",
m_state, ckRP, ((1<<ckRP)-1));
assert((m_state&((1<<ckRP)-1)) == ((1<<ckRP)-1));
}
if (m_min_time_before_precharge)
m_min_time_before_precharge--;
if (m_min_time_before_activate)
m_min_time_before_activate--;
break;
case DDR_ZQS:
assert((m_state&((1<<ckRP)-1)) == 0);
if (m_min_time_before_precharge)
m_min_time_before_precharge--;
if (m_min_time_before_activate)
m_min_time_before_activate--;
break;
case DDR_NOOP:
m_state <<= 1;
m_state |= (m_state&2)>>1;
m_state &= ((1<<ckRP)-1);
if (m_min_time_before_precharge)
m_min_time_before_precharge--;
if (m_min_time_before_activate)
m_min_time_before_activate--;
break;
default:
break;
}
}
int gbl_state, gbl_counts;
 
DDRSDRAMSIM::DDRSDRAMSIM(int lglen) {
m_memlen = (1<<(lglen-2));
m_mem = new unsigned[m_memlen];
m_reset_state = 0;
m_reset_counts= 0;
assert(NTIMESLOTS > ckCL+3);
m_bus = new BUSTIMESLOT[NTIMESLOTS];
for(int i=0; i<NTIMESLOTS; i++)
m_bus[i].m_used = 0;
for(int i=0; i<NTIMESLOTS; i++)
m_bus[i].m_rtt = 0;
m_busloc = 0;
}
 
unsigned DDRSDRAMSIM::operator()(int reset_n, int cke,
int csn, int rasn, int casn, int wen,
int dqs, int dm, int odt, int busoe,
int addr, int ba, int data) {
BUSTIMESLOT *ts, *nxtts;
int cmd = (reset_n?0:32)|(cke?0:16)|(csn?8:0)
|(rasn?4:0)|(casn?2:0)|(wen?1:0);
 
if ((m_reset_state!=0)&&(reset_n==0)) {
m_reset_state = 0;
m_reset_counts = 0;
} else if (m_reset_state < 16) {
switch(m_reset_state) {
case 0:
m_reset_counts++;
if (reset_n) {
assert(m_reset_counts > 40000);
m_reset_counts = 0;
m_reset_state = 1;
} break;
case 1:
m_reset_counts++;
if (cke) {
assert(m_reset_counts > 100000);
m_reset_counts = 0;
m_reset_state = 2;
} break;
case 2:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
assert(m_reset_counts > 147);
m_reset_counts = 0;
m_reset_state = 3;
assert(cmd == DDR_MRSET);
// Set MR2
assert(ba == 2);
assert(addr == DDR_MR2);
} break;
case 3:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
// assert(m_reset_counts > 3);
m_reset_counts = 0;
m_reset_state = 4;
assert(cmd == DDR_MRSET);
// Set MR1
assert(ba == 1);
assert(addr == DDR_MR1);
} break;
case 4:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
printf(PREFIX "::RESET-CMD[4]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr);
assert(m_reset_counts > 3);
m_reset_counts = 0;
m_reset_state = 5;
assert(cmd == DDR_MRSET);
// Set MR0
assert(ba == 0);
assert(addr == DDR_MR0);
} break;
case 5:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
printf(PREFIX "::RESET-CMD[5]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr);
assert(m_reset_counts > 11);
m_reset_counts = 0;
m_reset_state = 6;
assert(cmd == DDR_ZQS);
assert(addr == 0x400);
} break;
case 6:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
printf(PREFIX "::RESET-CMD[6]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr);
assert(m_reset_counts > 512);
m_reset_counts = 0;
m_reset_state = 7;
assert(cmd == DDR_PRECHARGE);
assert(addr == 0x400);
} break;
case 7:
m_reset_counts++;
assert(cke);
if (cmd != DDR_NOOP) {
printf(PREFIX "::RESET-CMD[7]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr);
assert(m_reset_counts > 3);
m_reset_counts = 0;
m_reset_state = 8;
assert(cmd == DDR_REFRESH);
m_clocks_since_refresh = 0;
} break;
case 8:
m_reset_counts++;
assert(cke);
assert(cmd == DDR_NOOP);
if (m_reset_counts > 140) {
m_reset_state = 16;
printf(PREFIX ": Leaving reset state\n");
}
break;
default:
break;
}
 
gbl_state = m_reset_state;
gbl_counts= m_reset_counts;
m_nrefresh_issued = nREF;
m_clocks_since_refresh++;
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(cmd, 0);
} else if (!cke) {
assert(0&&"Clock not enabled!");
} else if ((cmd == DDR_REFRESH)||(m_nrefresh_issued < (int)nREF)) {
if (DDR_REFRESH == cmd) {
m_clocks_since_refresh = 0;
if (m_nrefresh_issued >= (int)nREF)
m_nrefresh_issued = 1;
else
m_nrefresh_issued++;
} else {
m_clocks_since_refresh++;
assert(DDR_NOOP == cmd);
}
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(cmd,0);
 
if (m_nrefresh_issued == nREF)
printf(PREFIX "::Refresh cycle complete\n");
} else {
// In operational mode!!
 
m_clocks_since_refresh++;
assert(m_clocks_since_refresh < (int)ckREFIn);
switch(cmd) {
case DDR_MRSET:
assert(0&&"Modes should only be set in reset startup");
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(DDR_MRSET,0);
break;
case DDR_REFRESH:
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(DDR_REFRESH,0);
m_clocks_since_refresh = 0;
assert(0 && "Internal err: Refresh should be handled above");
break;
case DDR_PRECHARGE:
if (addr & 0x400) {
// Precharge all
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(DDR_PRECHARGE,0);
} else {
m_bank[ba].tick(DDR_PRECHARGE,0);
for(int i=0; i<NBANKS; i++)
if (ba != i)
m_bank[i].tick(DDR_NOOP,0);
}
break;
case DDR_ACTIVATE:
if (m_clocks_since_refresh < (int)ckRFC) {
printf(PREFIX "::ACTIVATE -- not enough clocks since refresh, %d < %d should be true\n", m_clocks_since_refresh, ckRFC);
assert(m_clocks_since_refresh >= (int)ckRFC);
}
printf(PREFIX "::Activating bank %d, address %08x\n", ba, addr);
m_bank[ba].tick(DDR_ACTIVATE,addr);
for(int i=0; i<NBANKS; i++)
if (i!=ba) m_bank[i].tick(DDR_NOOP,0);
break;
case DDR_WRITE:
{
// This SIM doesn't handle out of order writes
assert((addr&7)==0);
m_bank[ba].tick(DDR_WRITE, addr);
for(int i=0; i<NBANKS; i++)
if (i!=ba)m_bank[i].tick(DDR_NOOP,addr);
unsigned caddr = m_bank[ba].m_row;
caddr <<= 3;
caddr |= ba;
caddr <<= 10;
caddr |= addr;
caddr &= ~7;
caddr >>= 1;
 
BUSTIMESLOT *tp;
int offset = m_busloc+ckCL+1;
 
tp = &m_bus[(offset+0)&(NTIMESLOTS-1)];
// printf("Setting bus timeslots from (now=%d)+%d=%d to now+%d+3\n", m_busloc, ckCL,(m_busloc+ckCL)&(NTIMESLOTS-1), ckCL);
tp->m_addr = caddr ;
tp->m_used = 1;
tp->m_read = 0;
 
tp = &m_bus[(offset+1)&(NTIMESLOTS-1)];
tp->m_addr = caddr+1;
tp->m_used = 1;
tp->m_read = 0;
 
tp = &m_bus[(offset+2)&(NTIMESLOTS-1)];
tp->m_addr = caddr+2;
tp->m_used = 1;
tp->m_read = 0;
 
tp = &m_bus[(offset+3)&(NTIMESLOTS-1)];
tp->m_addr = caddr+3;
tp->m_used = 1;
tp->m_read = 0;
} break;
case DDR_READ:
{
// This SIM doesn't handle out of order reads
assert((addr&7)==0);
m_bank[ba].tick(DDR_READ, addr);
for(int i=0; i<NBANKS; i++)
if (i!=ba)m_bank[i].tick(DDR_NOOP,addr);
unsigned caddr = m_bank[ba].m_row;
caddr <<= 3;
caddr |= ba;
caddr <<= 10;
caddr |= addr;
caddr &= ~7;
caddr >>= 1;
 
BUSTIMESLOT *tp;
int offset = (m_busloc+ckCL+1)&(NTIMESLOTS-1);
tp = &m_bus[(offset)&(NTIMESLOTS-1)];
tp->m_data = m_mem[caddr];
tp->m_addr = caddr;
tp->m_used = 1;
tp->m_read = 1;
tp = &m_bus[(offset+1)&(NTIMESLOTS-1)];
tp->m_data = m_mem[caddr+1];
tp->m_addr = caddr+1;
tp->m_used = 1;
tp->m_read = 1;
 
tp = &m_bus[(offset+2)&(NTIMESLOTS-1)];
tp->m_data = m_mem[caddr+2];
tp->m_addr = caddr+2;
tp->m_used = 1;
tp->m_read = 1;
 
tp = &m_bus[(offset+3)&(NTIMESLOTS-1)];
tp->m_data = m_mem[caddr+3];
tp->m_addr = caddr+3;
tp->m_used = 1;
tp->m_read = 1;
} break;
case DDR_ZQS:
assert(0&&"Sim does not support ZQS outside of startup");
break;
case DDR_NOOP:
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(DDR_NOOP,addr);
break;
default: // We are deselecteda
for(int i=0; i<NBANKS; i++)
m_bank[i].tick(DDR_NOOP,addr);
break;
}
 
if (false) {
bool flag = false;
for(int i=0; i<5; i++) {
int bl = (m_busloc+1+i)&(NTIMESLOTS-1);
nxtts = &m_bus[bl];
if (nxtts->m_used) {
flag = true;
break;
}
} if (flag) {
printf("DQS = %d BUSLOC = %d\n", dqs, (m_busloc+1)&(NTIMESLOTS-1));
for(int i=0; i<5; i++) {
int bl = (m_busloc+1+i)&(NTIMESLOTS-1);
nxtts = &m_bus[bl];
printf("BUS[%2d] ", bl);
if (nxtts->m_used)
printf(" USED");
if (nxtts->m_read)
printf(" READ");
if (nxtts->m_rtt)
printf(" RTT");
printf("\n");
}}
}
 
ts = &m_bus[(m_busloc+1)&(NTIMESLOTS-1)];
if (dqs)
assert((ts->m_rtt)&&(m_last_rtt));
else if (!m_last_dqs)
assert(!m_last_rtt);
}
 
m_busloc = (m_busloc+1)&(NTIMESLOTS-1);
 
ts = &m_bus[m_busloc];
nxtts = &m_bus[(m_busloc+1)&(NTIMESLOTS-1)];
unsigned vl = ts->m_data;
assert( ((!ts->m_used)||(busoe))
|| ((ts->m_used)&&(ts->m_read)&&(!busoe))
|| ((ts->m_used)&&(!ts->m_read)&&(busoe))
);
 
m_last_dqs = dqs;
m_last_rtt = ts->m_rtt;
 
if (ts->m_used) {
if (ts->m_read)
assert((!dqs)&&(!m_last_dqs));
else
assert((dqs) && (m_last_dqs));
} else if (!nxtts->m_used)
assert(!dqs);
 
assert((!ts->m_used)||(ts->m_addr < (unsigned)m_memlen));
if ((ts->m_used)&&(!ts->m_read)&&(!dm)) {
printf(PREFIX "::Setting MEM[%08x] = %08x\n", ts->m_addr, data);
m_mem[ts->m_addr] = data;
}
 
m_bus[(m_busloc+3)&(NTIMESLOTS-1)].m_rtt = (odt)&&(reset_n);
ts->m_used = 0;
ts->m_read = 0;
ts->m_addr = -1;
ts->m_rtt = 0;
return (!busoe)?vl:data;
}
 
/sim/verilated/ddrsdramsim.h
0,0 → 1,83
////////////////////////////////////////////////////////////////////////////////
//
// Filename: ddrsdramsim.h
//
// Project: A wishbone controlled DDR3 SDRAM memory controller.
//
// Purpose:
//
// 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 DDRSDRAMSIM_H
#define DDRSDRAMSIM_H
 
#define DDR_MRSET 0
#define DDR_REFRESH 1
#define DDR_PRECHARGE 2
#define DDR_ACTIVATE 3
#define DDR_WRITE 4
#define DDR_READ 5
#define DDR_ZQS 6
#define DDR_NOOP 7
 
#define NBANKS 8
#define NTIMESLOTS 32
 
class BANKINFO {
public:
int m_state;
unsigned m_row, m_wcounter;
void tick(int cmd, unsigned addr=0);
};
 
class BUSTIMESLOT {
public:
int m_used, m_read, m_data, m_rtt;
unsigned m_addr;
};
 
class DDRSDRAMSIM {
int m_reset_state, m_reset_counts, m_memlen, m_busloc,
m_clocks_since_refresh, m_nrefresh_issued,
m_last_dqs, m_last_rtt;
unsigned *m_mem;
BANKINFO m_bank[8];
BUSTIMESLOT *m_bus;
int cmd(int,int,int,int);
public:
DDRSDRAMSIM(int lglen);
unsigned operator()(int, int,
int, int, int, int,
int, int, int, int,
int, int, int);
unsigned &operator[](unsigned addr) { return m_mem[addr]; };
};
 
#endif
/sim/verilated/enetctrl_tb.cpp
0,0 → 1,349
////////////////////////////////////////////////////////////////////////////////
//
// Filename: enetctrl_tb.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: To determine whether or not the enetctrl Verilog module works.
// Run this program with no arguments. If the last line output
// from it is "SUCCESS", you will know it works. Alternatively you can
// look at the return code. If the return code is 0 (EXIT_SUCCESS), then
// the test passed, ,otherrwise it failed.
//
// 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 "verilated_vcd_c.h"
#include "Venetctrl.h"
#include "enetctrlsim.h"
 
const int BOMBCOUNT = 2048;
 
class ENETCTRL_TB {
unsigned long m_tickcount;
Venetctrl *m_core;
ENETCTRLSIM *m_sim;
bool m_bomb;
VerilatedVcdC *m_trace;
 
public:
 
ENETCTRL_TB(void) {
m_core = new Venetctrl;
m_sim = new ENETCTRLSIM;
Verilated::traceEverOn(true);
m_trace = NULL;
m_tickcount = 0;
}
 
~ENETCTRL_TB(void) {
if (m_trace) {
m_trace->close();
delete m_trace;
}
}
 
int operator[](const int index) { return (*m_sim)[index]; }
 
void trace(const char *fname) {
if (!m_trace) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(fname);
}
}
 
void tick(void) {
m_core->i_mdio = (*m_sim)(0, m_core->o_mdclk,
((m_core->o_mdwe)&(m_core->o_mdio))
|((m_core->o_mdwe)?0:1));
 
#ifdef DEBUGGING_OUTPUT
printf("%08lx-WB: %s %s %s%s %s@0x%02x[%04x/%04x] -- %d[%d->(%d)->%d]",
m_tickcount,
(m_core->i_wb_cyc)?"CYC":" ",
(m_core->i_wb_stb)?"STB":" ",
(m_core->o_wb_stall)?"STALL":" ",
(m_core->o_wb_ack)?"ACK":" ",
(m_core->i_wb_we)?"W":"R",
(m_core->i_wb_addr)&0x01f, (m_core->i_wb_data)&0x0ffff,
(m_core->o_wb_data)&0x0ffff,
(m_core->o_mdclk), (m_core->o_mdio),
(m_core->o_mdwe), (m_core->i_mdio));
 
printf(" [%02x,%d%d%d,%x] ",
m_core->v__DOT__reg_pos,
0, // m_core->v__DOT__rclk,
m_core->v__DOT__zclk,
m_core->v__DOT__zreg_pos,
m_core->v__DOT__ctrl_state);
printf(" 0x%04x/0x%04x ", m_core->v__DOT__write_reg,
m_core->v__DOT__read_reg);
printf(" %s%s ",
(m_core->v__DOT__read_pending)?"R":" ",
(m_core->v__DOT__write_pending)?"W":" ");
 
printf(" %s:%08x,%2d,%08x ",
(m_sim->m_synched)?"S":" ",
m_sim->m_datareg, m_sim->m_halfword,
m_sim->m_outreg);
 
printf("\n");
#endif
 
if ((m_trace)&&(m_tickcount>0)) m_trace->dump(10*m_tickcount-2);
m_core->eval();
m_core->i_clk = 1;
m_core->eval();
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk = 0;
m_core->eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
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_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_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr= a & 0x01f;
 
if (m_core->o_wb_stall)
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall))
tick();
tick();
 
m_core->i_wb_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_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;
 
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_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr= a & 0x01f;
 
rdidx =0; cnt = 0;
 
do {
int s;
s = (m_core->o_wb_stall==0)?0:1;
tick();
if (!s)
m_core->i_wb_addr = (m_core->i_wb_addr+1)&0x1f;
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_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_stb = 1;
m_core->i_wb_we = 1;
m_core->i_wb_addr= a & 0x01f;
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_stb = 0;
 
while((errcount++ < BOMBCOUNT)&&(!m_core->o_wb_ack))
tick();
 
// Release the bus?
m_core->i_wb_cyc = 0;
m_core->i_wb_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;
for(unsigned stbcnt=0; stbcnt<ln; stbcnt++) {
m_core->i_wb_stb = 1;
m_core->i_wb_we = 1;
m_core->i_wb_addr= (a+stbcnt) & 0x01f;
m_core->i_wb_data= buf[stbcnt];
errcount = 0;
 
do {
tick(); if (m_core->o_wb_ack) nacks++;
} while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall));
}
 
m_core->i_wb_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_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);
ENETCTRL_TB *tb = new ENETCTRL_TB;
unsigned v;
// unsigned *rdbuf;
 
tb->trace("enetctrl.vcd");
 
tb->wb_tick();
tb->wb_write(0, 0x7f82);
if ((*tb)[0] != 0x7f82) {
printf("Somehow wrote a %04x, rather than 0x7f82\n", (*tb)[0]);
goto test_failure;
}
 
tb->wb_tick();
if ((v=tb->wb_read(0))!=0x7f82) {
printf("READ A %08x FROM THE CORE, NOT 0x7f82\n", v);
goto test_failure;
}
 
//
tb->wb_tick();
tb->wb_write(14, 0x5234);
 
tb->wb_tick();
if (tb->wb_read(14)!=0x5234)
goto test_failure;
 
printf("SUCCESS!!\n");
exit(EXIT_SUCCESS);
test_failure:
printf("FAIL-HERE\n");
for(int i=0; i<64; i++)
tb->tick();
printf("TEST FAILED\n");
exit(EXIT_FAILURE);
}
/sim/verilated/enetctrlsim.cpp
0,0 → 1,150
////////////////////////////////////////////////////////////////////////////////
//
// Filename: enetctrlsim.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
// 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 <assert.h>
#include "enetctrlsim.h"
 
ENETCTRLSIM::ENETCTRLSIM(void) {
m_consecutive_clocks = 0;
m_synched = false;
m_lastclk = 0;
m_lastout = 0;
m_tickcount = 0;
m_ticks_per_clock = 0;
m_halfword = 0;
m_datareg = -1;
PHY_ADDR = 1;
TICKS_PER_CLOCK = 4;
for(int i=0; i<ENET_MEMWORDS; i++)
m_mem[i] = 0;
m_outreg = -1;
}
 
int ENETCTRLSIM::operator()(int in_reset, int clk, int data) {
int posedge, negedge, output = 1;
 
posedge = ((clk)&&(!m_lastclk));
negedge = ((!clk)&&(m_lastclk));
 
m_tickcount++;
 
if (in_reset) {
m_consecutive_clocks = 0;
m_synched = false;
m_lastout = 1;
m_datareg = -1;
 
m_lastclk = clk;
 
return 1;
}
 
if (posedge) {
if ((data)&&(m_consecutive_clocks < 128))
m_consecutive_clocks++;
else if (!data)
m_consecutive_clocks = 0;
if ((m_tickcount != m_ticks_per_clock)
||(m_ticks_per_clock < TICKS_PER_CLOCK)) {
m_consecutive_clocks = 0;
m_synched = false;
} m_ticks_per_clock = m_tickcount;
m_tickcount = 0;
}
if (m_consecutive_clocks > 32) {
if (!m_synched)
printf("ENETCTRL: SYNCH!\n");
m_synched = true;
m_lastout = 1;
m_halfword = 0;
m_datareg = -1;
}
 
if ((posedge)&&(m_synched)) {
m_datareg = (m_datareg<<1)|(data&1);
if ((!m_halfword)&&((m_datareg&0x8000)==0)) {
printf("ENETCTRL::HALF-CMD: %08x\n", m_datareg);
m_halfword = 1;
int cmd = (m_datareg>>12)&0x0f;
int phy = (m_datareg>>7)&0x01f;
if ((cmd != 6)&&(cmd != 5))
printf("ENETCTRL: Unknown command, %d, expecting either 5 or 6\n", cmd);
if (phy != PHY_ADDR)
printf("ENETCTRL: Unknown PHY, %d, expecting %d\n", phy, PHY_ADDR);
if ((cmd == 6)&&(phy==PHY_ADDR)) {
int addr = (m_datareg>>2)&0x01f;
m_outreg = ((m_mem[addr]&0x0ffff)<<15)|0x080007fff;
printf("ENETCTRL: Sending %04x = MEM[%01x]\n",
m_mem[addr]&0x0ffff, addr);
}
} else if ((m_halfword)&&(m_halfword < 16)) {
m_halfword++;
} else if (m_halfword) {
printf("ENETCTRL::FULL-CMD: %08x\n", m_datareg);
m_halfword = 0;
int cmd = (m_datareg>>28)&0x0f;
int phy = (m_datareg>>23)&0x01f;
if ((cmd != 6)&&(cmd != 5))
printf("ENETCTRL: Unknown command, %d, expecting either 5 or 6\n", cmd);
if (phy != PHY_ADDR)
printf("ENETCTRL: Unknown PHY, %d, expecting %d\n", phy, PHY_ADDR);
if ((cmd==5)&&(phy==PHY_ADDR)) {
int addr;
 
if (m_datareg & 0x010000)
printf("ERR: ENETCTRL, write command and bit 16 is active!\n");
assert((m_datareg & 0x010000)==0);
addr = (m_datareg>>18)&0x1f;
m_mem[addr] = m_datareg & 0x0ffff;
printf("ENETCTRL: Setting MEM[%01x] = %04x\n",
addr, m_datareg&0x0ffff);
}
m_datareg = -1;
}
} else if (negedge) {
m_outreg = (m_outreg<<1)|1;
} output = (m_outreg&0x40000000)?1:0;
 
 
m_lastclk = clk;
return (data)&(output)&1;
}
 
int ENETCTRLSIM::operator[](int index) const {
return m_mem[index & (ENET_MEMWORDS-1)] & 0x0ffff;
}
/sim/verilated/enetctrlsim.h
0,0 → 1,59
////////////////////////////////////////////////////////////////////////////////
//
// Filename: enetsim.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 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 ENETCTRLSIM_H
#define ENETCTRLSIM_H
 
#define ENET_MEMWORDS 32
class ENETCTRLSIM {
int m_consecutive_clocks, m_lastout,
m_tickcount, m_ticks_per_clock, m_lastclk;
int TICKS_PER_CLOCK, PHY_ADDR;
int m_mem[ENET_MEMWORDS];
 
public:
bool m_synched;
int m_datareg, m_halfword, m_outreg;
ENETCTRLSIM(void);
~ENETCTRLSIM(void) {}
 
int operator()(int inreset, int clk, int data);
int operator[](int index) const;
};
 
#endif // ENETCTRLSIM_H
 
/sim/verilated/eqspiflash_tb.cpp
0,0 → 1,758
////////////////////////////////////////////////////////////////////////////////
//
// 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 "verilated_vcd_c.h"
#include "Veqspiflash.h"
#include "eqspiflashsim.h"
 
#define QSPIFLASH 0x0400000
const int BOMBCOUNT = 2048;
 
class EQSPIFLASH_TB {
unsigned long m_tickcount;
Veqspiflash *m_core;
EQSPIFLASHSIM *m_flash;
bool m_bomb;
VerilatedVcdC* m_trace;
 
public:
 
EQSPIFLASH_TB(void) {
Verilated::traceEverOn(true);
m_core = new Veqspiflash;
m_flash= new EQSPIFLASHSIM(24,true);
m_trace= NULL;
}
 
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 trace(const char *fname) {
if (!m_trace) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(fname);
}
}
 
void tick(void) {
// m_core->i_clk_82mhz = 0;
// m_core->eval();
m_core->i_qspi_dat = (*m_flash)(m_core->o_qspi_cs_n,
m_core->o_qspi_sck, m_core->o_qspi_dat);
 
m_core->i_clk_82mhz = 1;
m_core->eval();
// #define DEBUGGING_OUTPUT
#ifdef DEBUGGING_OUTPUT
printf("%08lx-WB: %s %s/%s %s %s[%s%s%s%s%s] %s %s@0x%08x[%08x/%08x] -- SPI %s%s[%x/%x](%d,%d)",
m_tickcount,
(m_core->i_wb_cyc)?"CYC":" ",
(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%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");
#endif
 
if ((m_trace)&&(m_tickcount>0)) m_trace->dump(10*m_tickcount-2);
m_core->i_clk_82mhz = 1;
m_core->eval();
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk_82mhz = 0;
m_core->eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
m_tickcount++;
 
/*
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();
} 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("RD-SETTING ERR TO TRUE!!!!!\n");
m_bomb = true;
} else if (!m_core->o_wb_ack) {
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n");
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("RDI-SETTING ERR TO TRUE!!!!! (errcount=%08x, THISBOMBCOUNT=%08x)\n", errcount, THISBOMBCOUNT);
m_bomb = true;
} else if (!m_core->o_wb_ack) {
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n");
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("WB-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("WBI-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("WBS-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->trace("eqspi.vcd");
 
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");
{
int nv;
nv = tb->wb_read(3);
printf("READ VCONF = %02x\n", nv);
printf("WRITING VCONF= %02x\n", nv | 0x08);
tb->wb_write(3, nv|0x08);
}
// tb->wb_write(0, 0x22000000);
 
printf("Attempting to read in Quad mode, using XIP mode\n");
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)&&(!tb->bombed()))
;
while((tb->wb_read(0)&0x80000000)&&(!tb->bombed()))
;
if (tb->bombed())
goto test_failure;
if (tb->wb_read(QSPIFLASH+1023) != rdbuf[0])
goto test_failure;
if (tb->wb_read(QSPIFLASH+2048) != rdbuf[1])
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);
}
/sim/verilated/eqspiflashsim.cpp
0,0 → 1,744
////////////////////////////////////////////////////////////////////////////////
//
// 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-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
 
#include "eqspiflashsim.h"
 
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(const int lglen, bool debug) {
int nsectors;
m_membytes = (1<<lglen);
m_memmask = (m_membytes - 1);
m_mem = new char[m_membytes];
m_pmem = new char[256];
m_otp = new char[65];
for(int i=0; i<65; i++)
m_otp[i] = 0x0ff;
m_otp[64] = 1;
m_otp_wp = false;
nsectors = m_membytes>>16;
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_vconfig = 0x83; // Volatile configuration register
m_nvconfig = 0x0fff; // Nonvolatile configuration register
m_quad_mode = EQSPIF_QMODE_SPI;
m_mode_byte = 0;
m_flagreg = 0x0a5;
 
m_debug = debug;
 
memset(m_mem, 0x0ff, m_membytes);
}
 
void EQSPIFLASHSIM::load(const unsigned addr, const char *fname) {
FILE *fp;
size_t len, nr = 0;
 
if (addr >= m_membytes)
return; // return void
 
// If not given, then length is from the given address until the end
// of the flash memory
len = m_membytes-addr*4;
 
if (NULL != (fp = fopen(fname, "r"))) {
nr = fread(&m_mem[addr*4], sizeof(char), len, fp);
fclose(fp);
if (nr == 0) {
fprintf(stderr, "EQSPI-FLASH: Could not read %s\n", fname);
perror("O/S Err:");
}
} else {
fprintf(stderr, "EQSPI-FLASH: Could not open %s\n", fname);
perror("O/S Err:");
}
 
for(unsigned i=nr; i<m_membytes; i++)
m_mem[i] = 0x0ff;
}
 
void EQSPIFLASHSIM::load(const uint32_t offset, const char *data, const uint32_t len) {
uint32_t moff = (offset & (m_memmask));
 
memcpy(&m_mem[moff], data, len);
}
 
#define QOREG(A) m_oreg = ((m_oreg & (~0x0ff))|(A&0x0ff))
 
int EQSPIFLASHSIM::operator()(const int csn, const int sck, const int dat) {
// 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 = EQSPIF_QMODE_SPI;
} else if (EQSPIF_WRCR == m_state) {
if (m_debug) printf("Actually writing volatile config register: VCONFIG = 0x%04x\n", m_vconfig);
if (m_debug) printf("CK = %d & 7 = %d\n", m_count, m_count & 0x07);
m_state = EQSPIF_IDLE;
} else if (EQSPIF_WRNVCONFIG == m_state) {
if (m_debug) printf("Actually writing nonvolatile config register: VCONFIG = 0x%02x\n", m_nvconfig);
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(unsigned i=0; i<m_membytes; i++)
m_mem[i] = 0x0ff;
} else if (m_state == EQSPIF_PROGRAM_OTP) {
// Program the One-Time Programmable (OTP memory
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) {
m_state = EQSPIF_IDLE;
if (m_mode_byte!=0)
m_quad_mode = EQSPIF_QMODE_SPI;
else {
if (m_quad_mode == EQSPIF_QMODE_SPI_ADDR)
m_quad_mode = EQSPIF_QMODE_SPI;
m_state = EQSPIF_XIP;
}
}
 
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) & m_memmask;
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 24 + 4*8) {// After the idle bits
m_state = EQSPIF_QUAD_READ;
if (m_debug) printf("EQSPI: QI/O Dummy = %04x\n", m_ireg);
m_mode_byte = (m_ireg>>24) & 0x10;
} m_oreg = 0;
} else if (m_count == 8) {
QOREG(0x0a5);
// 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;
if (m_debug) printf("EQSPI: SUBSECTOR_ERASE COMMAND\n");
}
break;
case 0x32: // QUAD Page program, 4 bits at a time
if (2 != (m_sreg & 0x203)) {
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_OREAD_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 VOLATILE CONFIG REGISTER: %02x\n", m_vconfig);
break;
case 0x85: // Read volatile config register
m_state = EQSPIF_RDCR;
if (m_debug) printf("EQSPI: READING VOLATILE CONFIG REGISTER: %02x\n", m_vconfig);
QOREG(m_vconfig);
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
m_state = EQSPIF_QUAD_IOREAD_CMD;
m_quad_mode = EQSPIF_QMODE_QSPI_ADDR;
break;
default:
printf("EQSPI: UNRECOGNIZED SPI FLASH CMD: %02x\n", m_ireg&0x0ff);
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+8+8) {
m_vconfig = m_ireg & 0x0ff;
if (m_debug) printf("Setting volatile config register to %08x\n", m_vconfig);
assert((m_vconfig & 0xfb)==0x8b);
} break;
case EQSPIF_WRNVCONFIG: // Write nonvolatile config register
if (m_count == 8+8+8) {
m_nvconfig = m_ireg & 0x0ffdf;
if (m_debug) printf("Setting nonvolatile config register to %08x\n", m_nvconfig);
assert((m_nvconfig & 0xffc5)==0x8fc5);
} break;
case EQSPIF_WREVCONFIG: // Write enhanced volatile config reg
if (m_count == 8+8) {
m_evconfig = m_ireg & 0x0ff;
if (m_debug) 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;
if (m_debug) printf("Setting lock register[%02x] to %d\n", m_addr, m_lockregs[m_addr]);
} break;
case EQSPIF_RDLOCK:
if (m_count == 24) {
m_addr = (m_ireg>>16)&0x0ff;
QOREG(m_lockregs[m_addr]);
if (m_debug) 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 VCONF = %02x\n", m_vconfig);
QOREG(m_creg);
break;
case EQSPIF_FAST_READ:
if (m_count < 32) {
QOREG(0x0c3);
} else if (m_count == 32) {
m_addr = m_ireg & m_memmask;
if (m_debug) printf("FAST READ, ADDR = %08x\n", m_addr);
QOREG(0x0c3);
if (m_addr & (~(m_memmask))) {
printf("EQSPI: ADDR = %08x ? !!\n", m_addr);
} assert((m_addr & (~(m_memmask)))==0);
} else if ((m_count >= 40)&&(0 == (m_sreg&0x01))) {
if ((m_debug)&&(m_count == 40))
printf("DUMMY BYTE COMPLETE ...\n");
QOREG(m_mem[m_addr++]);
if (m_debug) printf("SPIF[%08x] = %02x -> %02x\n", m_addr-1, m_mem[m_addr-1]&0x0ff, m_oreg);
} 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_IOREAD_CMD:
if (m_count == 32) {
m_addr = m_ireg & m_memmask;
if (m_debug) printf("EQSPI: QUAD I/O READ, ADDR = %06x (%02x:%02x:%02x:%02x)\n", m_addr,
(m_addr<0x1000000)?(m_mem[m_addr]&0x0ff):0,
(m_addr<0x0ffffff)?(m_mem[m_addr+1]&0x0ff):0,
(m_addr<0x0fffffe)?(m_mem[m_addr+2]&0x0ff):0,
(m_addr<0x0fffffd)?(m_mem[m_addr+3]&0x0ff):0);
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 8+24+8*4) {
QOREG(m_mem[m_addr++]);
m_quad_mode = EQSPIF_QMODE_QSPI_ADDR;
m_mode_byte = (m_ireg & 0x080);
m_state = EQSPIF_QUAD_READ;
} else {
m_oreg = 0;
}
break;
case EQSPIF_QUAD_OREAD_CMD:
if (m_count == 8+24) {
m_addr = m_ireg & m_memmask;
// printf("FAST READ, ADDR = %08x\n", m_addr);
if (m_debug) printf("EQSPI: QUAD READ, ADDR = %06x (%02x:%02x:%02x:%02x)\n", m_addr,
(m_addr<0x1000000)?(m_mem[m_addr]&0x0ff):0,
(m_addr<0x0ffffff)?(m_mem[m_addr+1]&0x0ff):0,
(m_addr<0x0fffffe)?(m_mem[m_addr+2]&0x0ff):0,
(m_addr<0x0fffffd)?(m_mem[m_addr+3]&0x0ff):0);
assert((m_addr & (~(m_memmask)))==0);
} else if (m_count == 8+24+4*8) {
QOREG(m_mem[m_addr]);
m_quad_mode = EQSPIF_QMODE_SPI_ADDR;
m_mode_byte = (m_ireg & 0x080);
if (m_debug) printf("EQSPI: (QUAD) MODE BYTE = %02x\n", m_mode_byte);
m_state = EQSPIF_QUAD_READ;
}
break;
case EQSPIF_QUAD_READ:
if ((m_count >= 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;
if (m_debug) printf("EQSPI/QR ... m_count = %d\n", m_count);
}
break;
case EQSPIF_PP:
if (m_count == 32) {
m_addr = m_ireg & m_memmask;
if (m_debug) printf("EQSPI: PAGE-PROGRAM ADDR = %06x\n", m_addr);
assert((m_addr & (~(m_memmask)))==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 & m_memmask;
m_quad_mode = EQSPIF_QMODE_SPI_ADDR;
if (m_debug) printf("EQSPI/QR: PAGE-PROGRAM ADDR = %06x\n", m_addr);
assert((m_addr & (~(m_memmask)))==0);
for(int i=0; i<256; i++)
m_pmem[i] = 0x0ff;
} else if (m_count >= 40) {
if (m_debug) printf("EQSPI: PROGRAM[%06x] = %02x\n", m_addr, m_ireg & 0x0ff);
m_pmem[m_addr & 0x0ff] = m_ireg & 0x0ff;
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("SUBSUBSECTOR_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;
}
 
/sim/verilated/eqspiflashsim.h
0,0 → 1,126
////////////////////////////////////////////////////////////////////////////////
//
// 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,2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef 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_OREAD_CMD,
EQSPIF_QUAD_IOREAD_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_vconfig, m_mode_byte, m_creg, m_membytes,
m_memmask, m_nvconfig, m_evconfig, m_flagreg, m_nxtout[4];
bool mode, m_debug, m_otp_wp;
 
typedef enum {
EQSPIF_QMODE_SPI = 0,
EQSPIF_QMODE_QSPI_ADDR,
EQSPIF_QMODE_SPI_ADDR
} QUAD_MODE;
QUAD_MODE m_quad_mode;
 
public:
EQSPIFLASHSIM(const int lglen = 24, bool debug = false);
void load(const char *fname) { load(0, fname); }
void load(const unsigned addr, const char *fname);
void load(const uint32_t offset, const char *data, const uint32_t len);
void debug(const bool dbg) { m_debug = dbg; }
bool debug(void) const { return m_debug; }
bool write_enabled(void) const { return m_debug; }
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
/sim/verilated/fastmaster_tb.cpp
0,0 → 1,1149
////////////////////////////////////////////////////////////////////////////////
//
// Filename: fastmaster_tb.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: This is a piped version of the testbench for the wishbone
// master verilog module, whether it be fastmaster.v or
// busmaster.v. Both fastmaster.v and busmaster.v are designed to be
// complete code sets implementing all of the functionality of the Digilent
// Arty board---save the hardware dependent features to include the DDR3
// memory. If done well, the programs talking to this one should be
// able to talk to the board and apply the same tests to the board itself.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <signal.h>
#include <time.h>
#include <ctype.h>
 
#include "verilated.h"
#ifdef FASTCLK
#include "Vfastmaster.h"
#define BASECLASS Vfastmaster
#error "This should never be incldued"
#else
#include "Vbusmaster.h"
#define BASECLASS Vbusmaster
#endif
 
#include "testb.h"
// #include "twoc.h"
#include "pipecmdr.h"
#include "eqspiflashsim.h"
#include "sdspisim.h"
#include "uartsim.h"
#include "enetctrlsim.h"
#include "memsim.h"
#ifdef OLEDSIM
#include "oledsim.h"
#endif
 
#include "zipelf.h"
#include "byteswap.h"
#include "port.h"
 
const int LGMEMSIZE = 28;
//
// Define where our memory is at, so we can load into it properly
// The following comes from the "skipaddr" reference in the I/O handling
// section. For example, if address line 23 is high, the address is for
// SDRAM base. The extra +2 is because each bus address references four
// bytes, and so there are two phantom address lines that need to be
// accounted for.
#define MEMBASE (1<<(15+2))
#define FLASHBASE (1<<(22+2))
#define SDRAMBASE (1<<(26+2))
//
// Setting the length to the base address works because the base address for
// each of these memory regions is given by a 'one' in the first bit not used
// by the respective device. If the memory is ever placed elsewhere, that will
// no longer work, and a proper length will need to be entered in here.
#define MEMLEN MEMBASE
#define FLASHLEN FLASHBASE
#define SDRAMLEN SDRAMBASE
 
 
// No particular "parameters" need definition or redefinition here.
class TESTBENCH : public PIPECMDR<BASECLASS> {
public:
unsigned long m_tx_busy_count;
EQSPIFLASHSIM m_flash;
SDSPISIM m_sdcard;
ENETCTRLSIM *m_mid;
UARTSIM m_uart;
MEMSIM m_ram;
#ifdef OLEDSIM_H
OLEDWIN m_oled;
#endif
int m_halt_in_count;
 
unsigned m_last_led, m_last_pic, m_last_tx_state, m_net_ticks;
time_t m_start_time;
bool m_last_writeout, m_cpu_started;
int m_last_bus_owner, m_busy, m_bomb;
unsigned long m_gps_err, m_gps_step, m_gps_newstep, m_traceticks;
unsigned m_gps_stepc;
bool m_done;
 
TESTBENCH(int fpgaport, int serialport, bool copy_to_stdout, bool debug)
: PIPECMDR(fpgaport, copy_to_stdout),
m_flash(24, debug), m_uart(serialport), m_ram(1<<26)
{
if (debug)
printf("Copy-to-stdout is %s\n", (copy_to_stdout)?"true":"false");
 
m_start_time = time(NULL);
m_mid = new ENETCTRLSIM;
m_cpu_started =false;
#ifdef OLEDSIM_H
Glib::signal_idle().connect(sigc::mem_fun((*this),&TESTBENCH::on_tick));
#endif
m_done = false;
m_bomb = 0;
m_traceticks = 0;
//
m_core->i_aux_cts_n = 0; // The sim doesnt use h/w flow control
//
m_halt_in_count = 0;
}
 
void reset(void) {
m_core->i_clk = 1;
m_core->eval();
}
 
void trace(const char *vcd_trace_file_name) {
fprintf(stderr, "Opening TRACE(%s)\n", vcd_trace_file_name);
opentrace(vcd_trace_file_name);
m_traceticks = 0;
}
 
void close(void) {
// TESTB<BASECLASS>::closetrace();
m_done = true;
}
 
void setsdcard(const char *fn) {
m_sdcard.load(fn);
}
 
void load(uint32_t addr, const char *buf, uint32_t len) {
if ((addr >= MEMBASE)&&(addr + len <= MEMBASE+MEMLEN)) {
char *bswapd = new char[len+8];
assert( ((len&3)==0) && ((addr&3)==0) );
memcpy(bswapd, buf, len);
byteswapbuf(len>>2, (uint32_t *)bswapd);
memcpy(&m_core->v__DOT__blkram__DOT__mem[addr-MEMBASE],
bswapd, len);
delete[] bswapd;
} else if ((addr >= FLASHBASE)&&(addr + len<= FLASHBASE+FLASHLEN))
m_flash.load(addr-FLASHBASE, buf, len);
else if ((addr >= SDRAMBASE)&&(addr + len<= SDRAMBASE+SDRAMLEN))
m_ram.load(addr-SDRAMBASE, buf, len);
else {
fprintf(stderr, "ERR: Address range %07x-%07x does not exist in memory\n",
addr, addr+len);
exit(EXIT_FAILURE);
}
}
 
bool gie(void) {
return (m_core->v__DOT__swic__DOT__thecpu__DOT__r_gie);
}
 
void dump(const uint32_t *regp) {
uint32_t uccv, iccv;
fflush(stderr);
fflush(stdout);
printf("ZIPM--DUMP: ");
if (gie())
printf("Interrupts-enabled\n");
else
printf("Supervisor mode\n");
printf("\n");
 
iccv = m_core->v__DOT__swic__DOT__thecpu__DOT__w_iflags;
uccv = m_core->v__DOT__swic__DOT__thecpu__DOT__w_uflags;
 
printf("sR0 : %08x ", regp[0]);
printf("sR1 : %08x ", regp[1]);
printf("sR2 : %08x ", regp[2]);
printf("sR3 : %08x\n",regp[3]);
printf("sR4 : %08x ", regp[4]);
printf("sR5 : %08x ", regp[5]);
printf("sR6 : %08x ", regp[6]);
printf("sR7 : %08x\n",regp[7]);
printf("sR8 : %08x ", regp[8]);
printf("sR9 : %08x ", regp[9]);
printf("sR10: %08x ", regp[10]);
printf("sR11: %08x\n",regp[11]);
printf("sR12: %08x ", regp[12]);
printf("sSP : %08x ", regp[13]);
printf("sCC : %08x ", iccv);
printf("sPC : %08x\n",regp[15]);
 
printf("\n");
 
printf("uR0 : %08x ", regp[16]);
printf("uR1 : %08x ", regp[17]);
printf("uR2 : %08x ", regp[18]);
printf("uR3 : %08x\n",regp[19]);
printf("uR4 : %08x ", regp[20]);
printf("uR5 : %08x ", regp[21]);
printf("uR6 : %08x ", regp[22]);
printf("uR7 : %08x\n",regp[23]);
printf("uR8 : %08x ", regp[24]);
printf("uR9 : %08x ", regp[25]);
printf("uR10: %08x ", regp[26]);
printf("uR11: %08x\n",regp[27]);
printf("uR12: %08x ", regp[28]);
printf("uSP : %08x ", regp[29]);
printf("uCC : %08x ", uccv);
printf("uPC : %08x\n",regp[31]);
printf("\n");
fflush(stderr);
fflush(stdout);
}
 
 
void execsim(const uint32_t imm) {
uint32_t *regp = m_core->v__DOT__swic__DOT__thecpu__DOT__regset;
int rbase;
rbase = (gie())?16:0;
 
fflush(stdout);
if ((imm & 0x03fffff)==0)
return;
// fprintf(stderr, "SIM-INSN(0x%08x)\n", imm);
if ((imm & 0x0fffff)==0x00100) {
// SIM Exit(0)
close();
exit(0);
} else if ((imm & 0x0ffff0)==0x00310) {
// SIM Exit(User-Reg)
int rcode;
rcode = regp[(imm&0x0f)+16] & 0x0ff;
close();
exit(rcode);
} else if ((imm & 0x0ffff0)==0x00300) {
// SIM Exit(Reg)
int rcode;
rcode = regp[(imm&0x0f)+rbase] & 0x0ff;
close();
exit(rcode);
} else if ((imm & 0x0fff00)==0x00100) {
// SIM Exit(Imm)
int rcode;
rcode = imm & 0x0ff;
close();
exit(rcode);
} else if ((imm & 0x0fffff)==0x002ff) {
// Full/unconditional dump
printf("SIM-DUMP\n");
dump(regp);
} else if ((imm & 0x0ffff0)==0x00200) {
// Dump a register
int rid = (imm&0x0f)+rbase;
printf("%8ld @%08x R[%2d] = 0x%08x\n", m_tickcount,
m_core->v__DOT__swic__DOT__thecpu__DOT__ipc,
rid, regp[rid]);
} else if ((imm & 0x0ffff0)==0x00210) {
// Dump a user register
int rid = (imm&0x0f);
printf("%8ld @%08x uR[%2d] = 0x%08x\n", m_tickcount,
m_core->v__DOT__swic__DOT__thecpu__DOT__ipc,
rid, regp[rid+16]);
} else if ((imm & 0x0ffff0)==0x00230) {
// SOUT[User Reg]
int rid = (imm&0x0f)+16;
printf("%c", regp[rid]&0x0ff);
} else if ((imm & 0x0fffe0)==0x00220) {
// SOUT[User Reg]
int rid = (imm&0x0f)+rbase;
printf("%c", regp[rid]&0x0ff);
} else if ((imm & 0x0fff00)==0x00400) {
// SOUT[Imm]
printf("%c", imm&0x0ff);
} else { // if ((insn & 0x0f7c00000)==0x77800000)
uint32_t immv = imm & 0x03fffff;
// Simm instruction that we dont recognize
// if (imm)
// printf("SIM 0x%08x\n", immv);
printf("SIM 0x%08x (ipc = %08x, upc = %08x)\n", immv,
m_core->v__DOT__swic__DOT__thecpu__DOT__ipc,
m_core->v__DOT__swic__DOT__thecpu__DOT__r_upc
);
} fflush(stdout);
}
 
bool on_tick(void) {
if (!m_done) {
tick();
return true; // Keep going 'til the kingdom comes
} else return false;
}
 
void tick(void) {
if (m_done)
return;
if ((m_tickcount & ((1<<28)-1))==0) {
double ticks_per_second = m_tickcount;
time_t seconds_passed = time(NULL)-m_start_time;
if (seconds_passed != 0) {
ticks_per_second /= (double)(time(NULL) - m_start_time);
printf(" ******** %.6f TICKS PER SECOND\n",
ticks_per_second);
}
}
 
if (m_halt_in_count > 0) {
if (m_halt_in_count-- <= 0) {
m_done = true;
}
}
 
if (TESTB<BASECLASS>::m_trace)
m_traceticks++;
 
// Set up the bus before any clock tick
#ifdef OLEDSIM_H
m_oled(m_core->o_oled_pmoden, m_core->o_oled_reset_n,
m_core->o_oled_vccen, m_core->o_oled_cs_n,
m_core->o_oled_sck, m_core->o_oled_dcn,
m_core->o_oled_mosi);
#endif
m_core->i_qspi_dat = m_flash(m_core->o_qspi_cs_n,
m_core->o_qspi_sck, m_core->o_qspi_dat);
 
m_core->i_mdio = (*m_mid)((m_core->o_net_reset_n==0)?1:0, m_core->o_mdclk,
((m_core->o_mdwe)&&(!m_core->o_mdio))?0:1);
 
/*
printf("MDIO: %d %d %d %d/%d -> %d\n",
m_core->o_net_reset_n,
m_core->o_mdclk,
m_core->o_mdwe,
m_core->o_mdio,
((m_core->o_mdwe)&&(!m_core->o_mdio))?0:1,
m_core->i_mdio);
*/
 
m_core->i_aux_rx = m_uart(m_core->o_aux_tx,
m_core->v__DOT__console__DOT__uart_setup);
m_core->i_gps_rx = 1;
 
m_core->i_sd_data = m_sdcard((m_core->o_sd_data&8)?1:0,
m_core->o_sd_sck, m_core->o_sd_cmd);
m_core->i_sd_data &= 1;
m_core->i_sd_data |= (m_core->o_sd_data&0x0e);
 
// Turn the network into a simple loopback device.
if (++m_net_ticks>5)
m_net_ticks = 0;
m_core->i_net_rx_clk = (m_net_ticks >= 2)&&(m_net_ticks < 5);
m_core->i_net_tx_clk = (m_net_ticks >= 0)&&(m_net_ticks < 3);
if (!m_core->i_net_rx_clk) {
m_core->i_net_dv = m_core->o_net_tx_en;
m_core->i_net_rxd = m_core->o_net_txd;
m_core->i_net_crs = m_core->o_net_tx_en;
} m_core->i_net_rxerr = 0;
if (!m_core->o_net_reset_n) {
m_core->i_net_dv = 0;
m_core->i_net_crs= 0;
}
 
m_ram.apply(m_core->o_ram_cyc, m_core->o_ram_stb,
m_core->o_ram_we, m_core->o_ram_addr,
m_core->o_ram_wdata, m_core->o_ram_sel,
m_core->i_ram_ack, m_core->i_ram_stall,
m_core->i_ram_rdata);
 
PIPECMDR::tick();
 
// Sim instructions
if ((m_core->v__DOT__swic__DOT__thecpu__DOT__op_sim)
&&(m_core->v__DOT__swic__DOT__thecpu__DOT__op_valid)
&&(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_ce)
&&(!m_core->v__DOT__swic__DOT__thecpu__DOT__new_pc)) {
//
execsim(m_core->v__DOT__swic__DOT__thecpu__DOT__op_sim_immv);
}
 
// #define DEBUGGING_OUTPUT
#ifdef DEBUGGING_OUTPUT
bool writeout = false;
 
/*
// Ethernet triggers
if (m_core->o_net_tx_en)
writeout = true;
if (m_core->v__DOT__netctrl__DOT__n_rx_busy)
writeout = true;
if (m_core->v__DOT__netctrl__DOT__r_txd_en)
writeout = true;
if (m_core->v__DOT__netctrl__DOT__w_rxwr)
writeout = true;
*/
 
/*
// GPS Clock triggers
if (m_core->v__DOT__ppsck__DOT__tick)
writeout = true;
if (m_core->v__DOT__gps_step != m_gps_step) {
writeout = true;
// printf("STEP");
} if (m_core->v__DOT__gps_err != m_gps_err) {
writeout = true;
// printf("ERR");
} if (m_core->v__DOT__ppsck__DOT__step_correction != m_gps_stepc) {
writeout = true;
// printf("DSTP");
} if (m_core->v__DOT__ppsck__DOT__getnewstep__DOT__genblk2__DOT__genblk1__DOT__r_out != m_gps_newstep)
writeout = true;
*/
 
/*
m_gps_step = m_core->v__DOT__gps_step;
m_gps_err = m_core->v__DOT__gps_err;
m_gps_stepc= m_core->v__DOT__ppsck__DOT__step_correction;
m_gps_newstep=m_core->v__DOT__ppsck__DOT__getnewstep__DOT__genblk2__DOT__genblk1__DOT__r_out;
*/
 
if (m_core->o_oled_cs_n == 0)
writeout = true;
if (m_core->o_oled_sck == 0)
writeout = true;
if (m_core->v__DOT__rgbctrl__DOT__dev_wr)
writeout = true;
if (m_core->v__DOT__rgbctrl__DOT__r_busy)
writeout = true;
if (m_core->v__DOT__rgbctrl__DOT__dev_busy)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__instruction_decoder__DOT__r_lock)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__genblk3__DOT__r_op_lock)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_prelock_stall)
writeout = true;
 
if (m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state != 0)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_bus_lock)
writeout = true;
 
 
/*
// GPS Tracking triggers
if (m_core->v__DOT__ppsck__DOT__err_tick)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__sub_tick)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__shift_tick)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__fltr_tick)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__config_tick)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__mpy_sync)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__mpy_sync_two)
writeout = true;
if (m_core->v__DOT__ppsck__DOT__delay_step_clk)
writeout = true;
*/
 
// if (m_core->v__DOT__wbu_cyc)
// writeout = true;
// if (m_core->v__DOT__dwb_cyc)
// writeout = true;
 
// CPU Debugging triggers
// Write out if the CPU is active at all
if (m_core->v__DOT__swic__DOT__thecpu__DOT__master_ce)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__dbgv)
writeout = true;
if ((m_core->v__DOT__swic__DOT__dbg_cyc)&&(m_core->v__DOT__swic__DOT__dbg_stb))
writeout = true;
if ((m_core->v__DOT__swic__DOT__dbg_cyc)&&(m_core->v__DOT__swic__DOT__dbg_ack))
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__pf_cyc)
writeout = true;
if (m_core->v__DOT__swic__DOT__thecpu__DOT__ipc < 0x10000000)
writeout = false;
 
writeout = true;
/*
*/
if ((writeout)||(m_last_writeout)) {
printf("%08lx:", m_tickcount);
 
/*
printf("%d/%02x %d/%02x%s ",
m_core->i_rx_stb, m_core->i_rx_data,
m_core->o_tx_stb, m_core->o_tx_data,
m_core->i_tx_busy?"/BSY":" ");
*/
 
// To get some understanding of what is on the bus,
// and hence some context for everything else,
// this offers a view of the bus.
printf("(%d,%d->%d)%s(%c:%s,%s->%s)",
m_core->v__DOT__wbu_cyc,
m_core->v__DOT__dwb_cyc, // was zip_cyc
m_core->v__DOT__wb_cyc,
"", // (m_core->v__DOT__wbu_zip_delay__DOT__r_stb)?"!":" ",
//
m_core->v__DOT__wbu_zip_arbiter__DOT__r_a_owner?'Z':'j',
(m_core->v__DOT__wbu_stb)?"1":" ", // WBU strobe
(m_core->v__DOT__swic__DOT__ext_stb)?"1":" ", // zip_stb
(m_core->v__DOT__wb_stb)?"1":" "); // m_core->v__DOT__wb_stb, output of delay(ed) strobe
//
printf("|%c[%08x/%08x]@%08x|%x %c%c%c",
(m_core->v__DOT__wb_we)?'W':'R',
m_core->v__DOT__wb_data,
m_core->v__DOT__dwb_idata,
m_core->v__DOT__wb_addr<<2,
m_core->v__DOT__wb_sel,
(m_core->v__DOT__dwb_ack)?'A':
(m_core->v__DOT____Vcellinp__genbus____pinNumber9)?'a':' ',
(m_core->v__DOT__dwb_stall)?'S':
(m_core->v__DOT____Vcellinp__genbus____pinNumber10)?'s':' ',
(m_core->v__DOT__wb_err)?'E':'.');
 
// CPU Pipeline debugging
printf("%s%s%s%s%s%s%s%s%s%s%s",
// (m_core->v__DOT__swic__DOT__dbg_ack)?"A":"-",
// (m_core->v__DOT__swic__DOT__dbg_stall)?"S":"-",
// (m_core->v__DOT__swic__DOT__sys_dbg_cyc)?"D":"-",
(m_core->v__DOT__swic__DOT__cpu_lcl_cyc)?"L":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_halted)?"Z":"-",
(m_core->v__DOT__swic__DOT__cpu_break)?"!":"-",
(m_core->v__DOT__swic__DOT__cmd_halt)?"H":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_gie)?"G":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__pf_cyc)?"P":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__pf_valid)?"V":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__pf_illegal)?"i":" ",
(m_core->v__DOT__swic__DOT__thecpu__DOT__new_pc)?"N":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__domem__DOT__r_wb_cyc_gbl)?"G":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__domem__DOT__r_wb_cyc_lcl)?"L":"-");
printf("|%s%s%s%s%s%s",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_dcd_valid)?"D":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__dcd_ce)?"d":"-",
"x", // (m_core->v__DOT__swic__DOT__thecpu__DOT__dcdA_stall)?"A":"-",
"x", // (m_core->v__DOT__swic__DOT__thecpu__DOT__dcdB_stall)?"B":"-",
"x", // (m_core->v__DOT__swic__DOT__thecpu__DOT__dcdF_stall)?"F":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__dcd_illegal)?"i":"-");
printf("|%s%s%s%s%s%s%s%s%s%s",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_valid)?"O":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_ce)?"k":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_stall)?"s":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_illegal)?"i":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_op_break)?"B":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__genblk3__DOT__r_op_lock)?"L":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_op_pipe)?"P":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_break_pending)?"p":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_op_gie)?"G":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_valid_alu)?"A":"-");
printf("|%s%s%s%d",
(m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_prelock_stall)?"P":".",
(m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_prelock_primed)?"p":".",
(m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_bus_lock)?"L":".",
m_core->v__DOT__swic__DOT__thecpu__DOT__genblk9__DOT__r_bus_lock);
printf("|%s%s%s%s%s",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_ce)?"a":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_stall)?"s":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__doalu__DOT__r_busy)?"B":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_alu_gie)?"G":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_alu_illegal)?"i":"-");
printf("|%s%s%s%2x %s%s%s %2d %2d",
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_valid_mem)?"M":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__mem_ce)?"m":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__adf_ce_unconditional)?"!":"-",
(m_core->v__DOT__swic__DOT__cmd_addr),
(m_core->v__DOT__swic__DOT__thecpu__DOT__bus_err)?"BE":" ",
(m_core->v__DOT__swic__DOT__thecpu__DOT__ibus_err_flag)?"IB":" ",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_ubus_err_flag)?"UB":" ",
m_core->v__DOT__swic__DOT__thecpu__DOT__domem__DOT__rdaddr,
m_core->v__DOT__swic__DOT__thecpu__DOT__domem__DOT__wraddr);
printf("|%s%s",
(m_core->v__DOT__swic__DOT__thecpu__DOT__div_busy)?"D":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__div_error)?"E":"-");
printf("|%s%s[%2x]%08x",
(m_core->v__DOT__swic__DOT__thecpu__DOT__wr_reg_ce)?"W":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__wr_flags_ce)?"F":"-",
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_reg_id,
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_gpreg_vl);
 
// Program counter debugging
printf(" PC0x%08x/%08x/%08x-I:%08x %s0x%08x%s",
m_core->v__DOT__swic__DOT__thecpu__DOT__pf_pc,
m_core->v__DOT__swic__DOT__thecpu__DOT__ipc,
m_core->v__DOT__swic__DOT__thecpu__DOT__r_upc,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf_instruction,
(m_core->v__DOT__swic__DOT__thecpu__DOT__instruction_decoder__DOT__genblk3__DOT__r_early_branch)?"EB":
((m_core->v__DOT__swic__DOT__thecpu__DOT__instruction_decoder__DOT__genblk3__DOT__r_ljmp)?"JM":" "),
m_core->v__DOT__swic__DOT__thecpu__DOT__instruction_decoder__DOT__genblk3__DOT__r_branch_pc<<2,
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_clear_icache)?"-CLRC":" "
);
// More in-depth
printf(" [%c%08x,%c%08x,%c%08x]",
(m_core->v__DOT__swic__DOT__thecpu__DOT__r_dcd_valid)?'D':'-',
m_core->v__DOT__swic__DOT__thecpu__DOT__dcd_pc<<2,
(m_core->v__DOT__swic__DOT__thecpu__DOT__op_valid)?'O':'-',
m_core->v__DOT__swic__DOT__thecpu__DOT__op_pc<<2,
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_valid)?'A':'-',
m_core->v__DOT__swic__DOT__thecpu__DOT__r_alu_pc<<2);
// Prefetch debugging
printf(" [PC%08x,LST%08x]->[%d%s%s](%d,%08x/%08x)->%08x@%08x",
m_core->v__DOT__swic__DOT__thecpu__DOT__pf_pc<<2,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__lastpc<<2,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__rvsrc,
(m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__rvsrc)
?((m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_v_from_pc)?"P":" ")
:((m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_v_from_pc)?"p":" "),
(!m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__rvsrc)
?((m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_v_from_last)?"l":" ")
:((m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_v_from_last)?"L":" "),
m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__isrc,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_pc_cache,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf__DOT__r_last_cache,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf_instruction,
m_core->v__DOT__swic__DOT__thecpu__DOT__pf_instruction_pc<<2);
 
// Decode Stage debugging
// (nothing)
 
// Op Stage debugging
printf(" (Op %02x,%02x)(%08x,%08x->%02x)",
m_core->v__DOT__swic__DOT__thecpu__DOT__dcd_opn,
m_core->v__DOT__swic__DOT__thecpu__DOT__r_op_opn,
m_core->v__DOT__swic__DOT__thecpu__DOT__op_Av,
m_core->v__DOT__swic__DOT__thecpu__DOT__op_Bv,
m_core->v__DOT__swic__DOT__thecpu__DOT__r_op_R);
 
printf(" %s[",
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_reg_ce?"WR":"--");
{ int reg;
const static char *rnames[] = {
"sR0", "sR1", "sR2", "sR3",
"sR4", "sR5", "sR6", "sR7",
"sR8", "sR9", "sRa", "sRb",
"sRc", "sSP", "sCC", "sPC",
"uR0", "uR1", "uR2", "uR3",
"uR4", "uR5", "uR6", "uR7",
"uR8", "uR9", "uRa", "uRb",
"uRc", "uSP", "uCC", "uPC"
};
reg = m_core->v__DOT__swic__DOT__thecpu__DOT__wr_reg_id & 0x01f;
printf("%s", rnames[reg]);
}
printf("]=%08x(%08x)",
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_gpreg_vl,
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_spreg_vl
);
 
printf(" %s[%s%s%s%s]",
m_core->v__DOT__swic__DOT__thecpu__DOT__wr_flags_ce?"F":"-",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_flags&1)?"Z":".",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_flags&2)?"C":".",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_flags&4)?"N":".",
(m_core->v__DOT__swic__DOT__thecpu__DOT__alu_flags&8)?"V":".");
 
/*
printf(" DBG%s%s%s[%s/%02x]=%08x/%08x",
(m_core->v__DOT__swic__DOT__dbg_cyc)?"CYC":" ",
(m_core->v__DOT__swic__DOT__dbg_stb)?"STB":((m_core->v__DOT__swic__DOT__dbg_ack)?"ACK":" "),
((m_core->v__DOT__swic__DOT__dbg_cyc)&&(m_core->v__DOT__swic__DOT__dbg_stb))?((m_core->v__DOT__swic__DOT__dbg_we)?"-W":"-R"):" ",
(!m_core->v__DOT__swic__DOT__dbg_cyc) ? " ":
((m_core->v__DOT__swic__DOT__dbg_addr)?"D":"C"),
(m_core->v__DOT__swic__DOT__cmd_addr),
(m_core->v__DOT__swic__DOT__dbg_idata),
m_core->v__DOT__zip_dbg_data);
 
printf(" %s,0x%08x", (m_core->i_ram_ack)?"RCK":" ",
m_core->i_ram_rdata);
*/
 
 
/*
printf(" SDSPI[%d,%d(%d),(%d)]",
m_core->v__DOT__sdcard_controller__DOT__r_cmd_busy,
m_core->v__DOT__sdcard_controller__DOT__r_sdspi_clk,
m_core->v__DOT__sdcard_controller__DOT__r_cmd_state,
m_core->v__DOT__sdcard_controller__DOT__r_rsp_state);
printf(" LL[%d,%2x->CK=%d/%2x,%s,ST=%2d,TX=%2x,RX=%2x->%d,%2x] ",
m_core->v__DOT__sdcard_controller__DOT__ll_cmd_stb,
m_core->v__DOT__sdcard_controller__DOT__ll_cmd_dat,
m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_z_counter,
// (m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_clk_counter==0)?1:0,
m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_clk_counter,
(m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_idle)?"IDLE":" ",
m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_state,
m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_byte,
m_core->v__DOT__sdcard_controller__DOT__lowlevel__DOT__r_ireg,
m_core->v__DOT__sdcard_controller__DOT__ll_out_stb,
m_core->v__DOT__sdcard_controller__DOT__ll_out_dat
);
printf(" CRC=%02x/%2d",
m_core->v__DOT__sdcard_controller__DOT__r_cmd_crc,
m_core->v__DOT__sdcard_controller__DOT__r_cmd_crc_cnt);
printf(" SPI(%d,%d,%d/%d,%d)->?",
m_core->o_sf_cs_n,
m_core->o_sd_cs_n,
m_core->o_spi_sck, m_core->v__DOT__sdcard_sck,
m_core->o_spi_mosi);
 
printf(" CK=%d,LN=%d",
m_core->v__DOT__sdcard_controller__DOT__r_sdspi_clk,
m_core->v__DOT__sdcard_controller__DOT__r_lgblklen);
 
 
if (m_core->v__DOT__sdcard_controller__DOT__r_use_fifo){
printf(" FIFO");
if (m_core->v__DOT__sdcard_controller__DOT__r_fifo_wr)
printf("-WR(%04x,%d,%d,%d)",
m_core->v__DOT__sdcard_controller__DOT__fifo_rd_crc_reg,
m_core->v__DOT__sdcard_controller__DOT__fifo_rd_crc_stb,
m_core->v__DOT__sdcard_controller__DOT__ll_fifo_pkt_state,
m_core->v__DOT__sdcard_controller__DOT__r_have_data_response_token);
else
printf("-RD(%04x,%d,%d,%d)",
m_core->v__DOT__sdcard_controller__DOT__fifo_wr_crc_reg,
m_core->v__DOT__sdcard_controller__DOT__fifo_wr_crc_stb,
m_core->v__DOT__sdcard_controller__DOT__ll_fifo_wr_state,
m_core->v__DOT__sdcard_controller__DOT__ll_fifo_wr_complete
);
}
*/
 
/*
// Network debugging
printf("ETH[TX:%s%s%x%s]",
(m_core->i_net_tx_clk)?"CK":" ",
(m_core->o_net_tx_en)?" ":"(",
m_core->o_net_txd,
(m_core->o_net_tx_en)?" ":")");
printf("/%s(%04x,%08x[%08x])",
(m_core->v__DOT__netctrl__DOT__n_tx_busy)?"BSY":" ",
m_core->v__DOT__netctrl__DOT__n_tx_addr,
m_core->v__DOT__netctrl__DOT__n_next_tx_data,
m_core->v__DOT__netctrl__DOT__n_tx_data);
printf("[RX:%s%s%s%s%x%s]",
(m_core->i_net_rx_clk)?"CK":" ",
(m_core->i_net_crs)?"CR":" ",
(m_core->i_net_rxerr)?"ER":" ",
(m_core->i_net_dv)?" ":"(",
m_core->i_net_rxd,
(m_core->i_net_dv)?" ":")");
printf("%s%s%s",
(m_core->v__DOT__netctrl__DOT__n_rx_valid)?"V":" ",
(m_core->v__DOT__netctrl__DOT__n_rx_clear)?"C":" ",
(m_core->v__DOT__netctrl__DOT__n_rx_net_err)?"E":" ");
printf("/%s(%04x,%s%08x)",
(m_core->v__DOT__netctrl__DOT__n_rx_busy)?"BSY":" ",
m_core->v__DOT__netctrl__DOT__w_rxaddr,
(m_core->v__DOT__netctrl__DOT__w_rxwr)?"W":" ",
m_core->v__DOT__netctrl__DOT__w_rxdata);
 
printf(" TXMAC %x%s -> %2x -> %x%s",
m_core->v__DOT__netctrl__DOT__r_txd,
(m_core->v__DOT__netctrl__DOT__r_txd_en)?"!":" ",
(m_core->v__DOT__netctrl__DOT__txmaci__DOT__r_pos),
m_core->v__DOT__netctrl__DOT__w_macd,
(m_core->v__DOT__netctrl__DOT__w_macen)?"!":" ");
printf(" TXCRC %x%s ->%2x/0x%08x-> %x%s",
m_core->v__DOT__netctrl__DOT__w_padd,
(m_core->v__DOT__netctrl__DOT__w_paden)?"!":" ",
m_core->v__DOT__netctrl__DOT__txcrci__DOT__r_p,
m_core->v__DOT__netctrl__DOT__txcrci__DOT__r_crc,
m_core->v__DOT__netctrl__DOT__w_txcrcd,
(m_core->v__DOT__netctrl__DOT__w_txcrcen)?"!":" ");
 
printf(" RXCRC %x%s -> 0x%08x/%2x/%2x/%s -> %x%s",
m_core->v__DOT__netctrl__DOT__w_npred,
(m_core->v__DOT__netctrl__DOT__w_npre)?"!":" ",
m_core->v__DOT__netctrl__DOT__rxcrci__DOT__r_crc,
(m_core->v__DOT__netctrl__DOT__rxcrci__DOT__r_mq),
(m_core->v__DOT__netctrl__DOT__rxcrci__DOT__r_mp),
(m_core->v__DOT__netctrl__DOT__rxcrci__DOT__r_err)?"E":" ",
m_core->v__DOT__netctrl__DOT__w_rxcrcd,
(m_core->v__DOT__netctrl__DOT__w_rxcrc)?"!":" ");
 
printf(" RXIP %x%s ->%4x%s->%4x/%2d/%2d/%s",
m_core->v__DOT__netctrl__DOT__w_rxcrcd,
(m_core->v__DOT__netctrl__DOT__w_rxcrc)?"!":" ",
(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_word)&0x0ffff,
(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_v)?"!":" ",
(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_check)&0x0ffff,
(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_idx),
(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_hlen),
(m_core->v__DOT__netctrl__DOT__w_iperr)?"E"
:(m_core->v__DOT__netctrl__DOT__rxipci__DOT__r_ip)?" ":"z");
printf(" RXMAC %x%s ->%2x-> %x%s",
m_core->v__DOT__netctrl__DOT__w_rxcrcd,
(m_core->v__DOT__netctrl__DOT__w_rxcrc)?"!":" ",
(m_core->v__DOT__netctrl__DOT__rxmaci__DOT__r_p)&0x0ff,
m_core->v__DOT__netctrl__DOT__w_rxmacd,
(m_core->v__DOT__netctrl__DOT__w_rxmac)?"!":" ");
*/
 
/*
// Flash debugging support
printf("%s/%s %s %s[%s%s%s%s%s] %s@%08x[%08x/%08x] -- SPI %s%s[%x/%x](%d,%d)",
((m_core->v__DOT__wb_stb)&&((m_core->v__DOT__skipaddr>>3)==1))?"D":" ",
((m_core->v__DOT__wb_stb)&&(m_core->v__DOT__flctl_sel))?"C":" ",
(m_core->v__DOT__flashmem__DOT__bus_wb_stall)?"STALL":" ",
(m_core->v__DOT__flash_ack)?"ACK":" ",
(m_core->v__DOT__flashmem__DOT__bus_wb_ack)?"BS":" ",
(m_core->v__DOT__flashmem__DOT__rd_data_ack)?"RD":" ",
(m_core->v__DOT__flashmem__DOT__ew_data_ack)?"EW":" ",
(m_core->v__DOT__flashmem__DOT__id_data_ack)?"ID":" ",
(m_core->v__DOT__flashmem__DOT__ct_data_ack)?"CT":" ",
(m_core->v__DOT__wb_we)?"W":"R",
(m_core->v__DOT__wb_addr),
(m_core->v__DOT__wb_data),
(m_core->v__DOT__flash_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(" REQ[%s%s%s%s]",
m_core->v__DOT__flashmem__DOT__rd_qspi_req?"RD":" ",
m_core->v__DOT__flashmem__DOT__ew_qspi_req?"EW":" ",
m_core->v__DOT__flashmem__DOT__id_qspi_req?"ID":" ",
m_core->v__DOT__flashmem__DOT__ct_qspi_req?"CT":" ");
 
printf(" %s[%s%2d%s%s0x%08x]",
(m_core->v__DOT__flashmem__DOT__spi_wr)?"CMD":" ",
(m_core->v__DOT__flashmem__DOT__spi_hold)?"HLD":" ",
(m_core->v__DOT__flashmem__DOT__spi_len+1)*8,
(m_core->v__DOT__flashmem__DOT__spi_dir)?"RD":"WR",
(m_core->v__DOT__flashmem__DOT__spi_spd)?"Q":" ",
m_core->v__DOT__flashmem__DOT__spi_word);
 
printf(" STATE[%2x%s,%2x%s,%2x%s,%2x%s]",
m_core->v__DOT__flashmem__DOT__rdproc__DOT__rd_state, (m_core->v__DOT__flashmem__DOT__rd_spi_wr)?"W":" ",
m_core->v__DOT__flashmem__DOT__ewproc__DOT__wr_state, (m_core->v__DOT__flashmem__DOT__ew_spi_wr)?"W":" ",
m_core->v__DOT__flashmem__DOT__idotp__DOT__id_state, (m_core->v__DOT__flashmem__DOT__id_spi_wr)?"W":" ",
m_core->v__DOT__flashmem__DOT__ctproc__DOT__ctstate, (m_core->v__DOT__flashmem__DOT__ct_spi_wr)?"W":" ");
printf("%s%s%s%s",
(m_core->v__DOT__flashmem__DOT__rdproc__DOT__accepted)?"RD-ACC":"",
(m_core->v__DOT__flashmem__DOT__ewproc__DOT__accepted)?"EW-ACC":"",
(m_core->v__DOT__flashmem__DOT__idotp__DOT__accepted)?"ID-ACC":"",
(m_core->v__DOT__flashmem__DOT__ctproc__DOT__accepted)?"CT-ACC":"");
 
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__pending)?" PENDING":"",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__lcl_key)?" KEY":"",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__ctreg_stb)?" CTSTB":"",
(m_core->v__DOT__flashmem__DOT__bus_ctreq)?" BUSCTRL":"",
(m_core->v__DOT__flashmem__DOT__bus_other_req)?" BUSOTHER":"",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__wp)?" WRWP":"",
(m_core->v__DOT__flashmem__DOT__bus_wip)?" WIP":"",
(m_core->v__DOT__flashmem__DOT__ewproc__DOT__cyc)?" WRCYC":"",
(m_core->v__DOT__flashmem__DOT__bus_pipewr)?" WRPIPE":"",
(m_core->v__DOT__flashmem__DOT__bus_endwr)?" ENDWR":"",
(m_core->v__DOT__flashmem__DOT__ct_ack)?" CTACK":"",
(m_core->v__DOT__flashmem__DOT__rd_bus_ack)?" RDACK":"",
(m_core->v__DOT__flashmem__DOT__id_bus_ack)?" IDACK":"",
(m_core->v__DOT__flashmem__DOT__ew_bus_ack)?" EWACK":"",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__lcl_ack)?" LCLACK":"",
(m_core->v__DOT__flashmem__DOT__rdproc__DOT__r_leave_xip)?" LVXIP":"",
(m_core->v__DOT__flashmem__DOT__preproc__DOT__new_req)?" NREQ":"");
*/
 
 
/*
// Debugging the GPS tracking circuit
printf("COUNT %016lx STEP %016lx+%08x->%016lx ERR %016lx %s",
m_core->v__DOT__gps_now,
m_core->v__DOT__gps_step,
m_core->v__DOT__ppsck__DOT__step_correction,
m_core->v__DOT__ppsck__DOT__getnewstep__DOT__genblk2__DOT__genblk1__DOT__r_out,
m_core->v__DOT__gps_err,
(m_core->v__DOT__ppsck__DOT__tick)?"TICK":" ");
*/
 
 
/*
// Debug the OLED
 
{ const char *pwr; int pwrk;
if (m_core->o_oled_pmoden) {
if (!m_core->o_oled_reset_n)
pwr = "RST";
else if (m_core->o_oled_vccen)
pwr = "ON ";
else
pwr = "VIO";
} else if (m_core->o_oled_vccen)
pwr = "ERR";
else
pwr = "OFF";
pwrk = (m_core->o_oled_reset_n)?4:0;
pwrk|= (m_core->o_oled_vccen)?2:0;
pwrk|= (m_core->o_oled_pmoden);
// First the top-level ports
printf(" OLED[%s/%d,%s%s%s-%d]",
pwr, pwrk,
(!m_core->o_oled_cs_n)?"CS":" ",
(m_core->o_oled_sck)?"CK":" ",
(m_core->o_oled_dcn)?"/D":"/C",
(m_core->o_oled_mosi));
}
// Now the low-level internals
printf("LL[");
switch(m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__state){
case 0: printf("I,"); break;
case 1: printf("S,"); break;
case 2: printf("B,"); break;
case 3: printf("R,"); break;
case 4: printf("!,"); break;
case 5: printf(".,"); break;
default: printf("U%d",
m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__state);
}
printf("%2d,%s%2d,%08x]",
m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__spi_len,
(m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__pre_last_counter)?"P":" ",
 
m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__counter,
m_core->v__DOT__rgbctrl__DOT__lwlvl__DOT__r_word);
printf("[%s%s%s/%2d/%d]",
(m_core->v__DOT__rgbctrl__DOT__dev_wr)?"W":" ",
(m_core->v__DOT__rgbctrl__DOT__r_busy)?"BSY":" ",
(m_core->v__DOT__rgbctrl__DOT__dev_busy)?"D-BSY":" ",
m_core->v__DOT__rgbctrl__DOT__r_len,
m_core->v__DOT__rgbctrl__DOT__dev_len);
printf((m_core->v__DOT__oled_int)?"I":" "); // And the interrupt
*/
 
/*
// Debug the DMA
printf(" DMAC[%d]: %08x/%08x/%08x(%03x)%d%d%d%d -- (%d,%d,%c)%c%c:@%08x-[%4d,%4d/%4d,%4d-#%4d]%08x",
m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state,
m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_waddr,
m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_raddr,
m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_len,
m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_blocklen_sub_one,
m_core->v__DOT__swic__DOT__dma_controller__DOT__last_read_request,
m_core->v__DOT__swic__DOT__dma_controller__DOT__last_read_ack,
m_core->v__DOT__swic__DOT__dma_controller__DOT__last_write_request,
m_core->v__DOT__swic__DOT__dma_controller__DOT__last_write_ack,
m_core->v__DOT__swic__DOT__dc_cyc,
// m_core->v__DOT__swic__DOT__dc_stb,
(m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state == 2)?1:0,
 
((m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state == 4)
||(m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state == 5)
||(m_core->v__DOT__swic__DOT__dma_controller__DOT__dma_state == 6))?'W':'R',
//(m_core->v__DOT__swic__DOT__dc_we)?'W':'R',
(m_core->v__DOT__swic__DOT__dc_ack)?'A':' ',
(m_core->v__DOT__swic__DOT__dc_stall)?'S':' ',
m_core->v__DOT__swic__DOT__dc_addr,
m_core->v__DOT__swic__DOT__dma_controller__DOT__rdaddr,
m_core->v__DOT__swic__DOT__dma_controller__DOT__nread,
m_core->v__DOT__swic__DOT__dma_controller__DOT__nracks,
m_core->v__DOT__swic__DOT__dma_controller__DOT__nwacks,
m_core->v__DOT__swic__DOT__dma_controller__DOT__nwritten,
m_core->v__DOT__swic__DOT__dc_data);
printf((m_core->v__DOT__swic__DOT__dma_controller__DOT__trigger)?"T":" ");
printf((m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_incs)?"+":".");
printf((m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_incd)?"+":".");
printf("%s[%2x]",
(m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_on_dev_trigger)?"!":" ",
(m_core->v__DOT__swic__DOT__dma_controller__DOT__cfg_dev_trigger));
*/
 
printf(" INT:0x%08x/0x%08x",
m_core->v__DOT__swic__DOT__main_int_vector,
m_core->v__DOT__swic__DOT__alt_int_vector);
 
printf("\n"); fflush(stdout);
} m_last_writeout = writeout;
#endif
 
/*
if (m_core->v__DOT__swic__DOT__cpu_break) {
m_bomb++;
} else if (m_bomb) {
if (m_bomb++ > 12)
m_done = true;
fprintf(stderr, "BREAK-BREAK-BREAK (m_bomb = %d)%s\n",
m_bomb, (m_done)?" -- DONE!":"");
}
*/
}
 
bool done(void) {
if (!m_trace)
return m_done;
else
return (m_done)||(m_traceticks > 6000000);
}
};
 
TESTBENCH *tb;
 
void busmaster_kill(int v) {
tb->close();
fprintf(stderr, "KILLED!!\n");
exit(EXIT_SUCCESS);
}
 
void usage(void) {
puts("USAGE: busmaster_tb [-cdpsth] <ZipElfProgram> <SDCardBackFile>\n"
"\n"
" -c Copies all FPGA control/command communications to the\n"
" standard output\n"
" -d Sets the debug flag. This turns on the trace feature, dumping\n"
" the trace to trace.vcd by default. This can be overridden by\n"
" the -t option\n"
" -h Prints this usage statement\n"
" -p # Sets the TCP/IP port number for the command port\n"
" -s # Sets the TCP/IP port number for the simulated serial port\n"
" -t <fname> Creates a VCD trace file with the name <fname>\n");
}
 
int main(int argc, char **argv) {
const char *elfload = NULL, *sdload = "/dev/zero",
*trace_file = NULL; // "trace.vcd";
bool debug_flag = false, willexit = false;
int fpga_port = FPGAPORT, serial_port = -(FPGAPORT+1);
int copy_comms_to_stdout = -1;
#ifdef OLEDSIM_H
Gtk::Main main_instance(argc, argv);
#endif
Verilated::commandArgs(argc, argv);
 
for(int argn=1; argn < argc; argn++) {
if (argv[argn][0] == '-') for(int j=1;
(j<512)&&(argv[argn][j]);j++) {
switch(tolower(argv[argn][j])) {
case 'c': copy_comms_to_stdout = 1; break;
case 'd': debug_flag = true;
if (trace_file == NULL)
trace_file = "trace.vcd";
break;
case 'p': fpga_port = atoi(argv[++argn]); j=1000; break;
case 's': serial_port=atoi(argv[++argn]); j=1000; break;
case 't': trace_file = argv[++argn]; j=1000; break;
case 'h': usage(); break;
default:
fprintf(stderr, "ERR: Unexpected flag, -%c\n\n",
argv[argn][j]);
usage();
break;
}
} else if (iself(argv[argn])) {
elfload = argv[argn];
} else if (0 == access(argv[argn], R_OK)) {
sdload = argv[argn];
} else {
fprintf(stderr, "ERR: Cannot read %s\n", argv[argn]);
perror("O/S Err:");
exit(EXIT_FAILURE);
}
}
 
if (elfload) {
if (serial_port < 0)
serial_port = 0;
if (copy_comms_to_stdout < 0)
copy_comms_to_stdout = 0;
tb = new TESTBENCH(fpga_port, serial_port,
(copy_comms_to_stdout)?true:false, debug_flag);
willexit = true;
} else {
if (serial_port < 0)
serial_port = -serial_port;
if (copy_comms_to_stdout < 0)
copy_comms_to_stdout = 1;
tb = new TESTBENCH(fpga_port, serial_port,
(copy_comms_to_stdout)?true:false, debug_flag);
}
 
if (debug_flag) {
printf("Opening Bus-master with\n");
printf("\tDebug Access port = %d\n", fpga_port);
printf("\tSerial Console = %d%s\n", serial_port,
(serial_port == 0) ? " (Standard output)" : "");
printf("\tDebug comms will%s be copied to the standard output%s.",
(copy_comms_to_stdout)?"":" not",
((copy_comms_to_stdout)&&(serial_port == 0))
? " as well":"");
printf("\tVCD File = %s\n", trace_file);
} if (trace_file)
tb->trace(trace_file);
signal(SIGINT, busmaster_kill);
 
tb->reset();
tb->setsdcard(sdload);
 
if (elfload) {
uint32_t entry;
ELFSECTION **secpp = NULL, *secp;
elfread(elfload, entry, secpp);
 
if (secpp) for(int i=0; secpp[i]->m_len; i++) {
secp = secpp[i];
tb->load(secp->m_start, secp->m_data, secp->m_len);
}
 
tb->m_core->v__DOT__swic__DOT__thecpu__DOT__ipc = entry;
tb->tick();
tb->m_core->v__DOT__swic__DOT__thecpu__DOT__ipc = entry;
tb->m_core->v__DOT__swic__DOT__cmd_halt = 0;
tb->tick();
}
 
#ifdef OLEDSIM_H
Gtk::Main::run(tb->m_oled);
#else
if (willexit) {
while(!tb->done())
tb->tick();
} else
while(!tb->done())
tb->tick();
 
#endif
 
exit(0);
}
 
/sim/verilated/memsim.cpp
0,0 → 1,157
////////////////////////////////////////////////////////////////////////////////
//
// Filename: memsim.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: This creates a memory like device to act on a WISHBONE bus.
// It doesn't exercise the bus thoroughly, but does give some
// exercise to the bus to see whether or not the bus master
// can control it.
//
// This particular version differs from the memsim version within the
// ZipCPU project in that there is a variable delay from request to
// completion.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include "memsim.h"
 
MEMSIM::MEMSIM(const unsigned int nwords, const unsigned int delay) {
unsigned int nxt;
for(nxt=1; nxt < nwords; nxt<<=1)
;
m_len = nxt; m_mask = nxt-1;
m_mem = new BUSW[m_len];
 
m_delay = delay;
for(m_delay_mask=1; m_delay_mask < delay; m_delay_mask<<=1)
;
m_fifo_ack = new int[m_delay_mask];
m_fifo_data = new BUSW[m_delay_mask];
for(unsigned i=0; i<m_delay_mask; i++)
m_fifo_ack[i] = 0;
m_delay_mask-=1;
m_head = 0; m_tail = (m_head - delay)&m_delay_mask;
}
 
MEMSIM::~MEMSIM(void) {
delete[] m_mem;
}
 
void MEMSIM::load(const char *fname) {
FILE *fp;
unsigned int nr;
 
fp = fopen(fname, "r");
if (!fp) {
fprintf(stderr, "Could not open/load file \'%s\'\n",
fname);
perror("O/S Err:");
fprintf(stderr, "\tInitializing memory with zero instead.\n");
nr = 0;
} else {
nr = fread(m_mem, sizeof(BUSW), m_len, fp);
fclose(fp);
 
if (nr != m_len) {
fprintf(stderr, "Only read %d of %d words\n",
nr, m_len);
fprintf(stderr, "\tFilling the rest with zero.\n");
}
}
 
for(; nr<m_len; nr++)
m_mem[nr] = 0l;
}
 
void MEMSIM::load(const unsigned int addr, const char *buf, const size_t len) {
memcpy(&m_mem[addr], buf, len);
}
 
void MEMSIM::apply(const uchar wb_cyc, const uchar wb_stb, const uchar wb_we,
const BUSW wb_addr, const BUSW wb_data, const uchar wb_sel,
unsigned char &o_ack, unsigned char &o_stall, BUSW &o_data) {
unsigned sel = 0;
 
if (wb_sel&0x8)
sel |= 0x0ff000000;
if (wb_sel&0x4)
sel |= 0x000ff0000;
if (wb_sel&0x2)
sel |= 0x00000ff00;
if (wb_sel&0x1)
sel |= 0x0000000ff;
m_head++; m_tail = (m_head - m_delay)&m_delay_mask;
m_head&=m_delay_mask;
o_ack = m_fifo_ack[m_tail];
o_data= m_fifo_data[m_tail];
 
m_fifo_ack[ m_head] = 0;
m_fifo_data[m_head] = 0;
 
o_stall= 0;
if ((wb_cyc)&&(wb_stb)) {
if (wb_we) {
if (sel == 0xffffffffu)
m_mem[wb_addr & m_mask] = wb_data;
else {
uint32_t memv = m_mem[wb_addr & m_mask];
memv &= ~sel;
memv |= (wb_data & sel);
m_mem[wb_addr & m_mask] = memv;
}
}
m_fifo_ack[m_head] = 1;
m_fifo_data[m_head] = m_mem[wb_addr & m_mask];
#ifdef DEBUG
printf("MEMBUS %s[%08x] = %08x\n",
(wb_we)?"W":"R",
wb_addr&m_mask,
m_mem[wb_addr&m_mask]);
#endif
// o_ack = 1;
}
 
#ifdef DEBUG
if (o_ack) {
printf("MEMBUS -- ACK %s 0x%08x - 0x%08x\n",
(wb_we)?"WRITE":"READ ",
wb_addr, o_data);
}
#endif
}
 
 
/sim/verilated/memsim.h
0,0 → 1,78
////////////////////////////////////////////////////////////////////////////////
//
// Filename: memsim.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: This creates a memory like device to act on a WISHBONE bus.
// It doesn't exercise the bus thoroughly, but does give some
// exercise to the bus to see whether or not the bus master
// can control it.
//
// This particular version differs from the memsim version within the
// ZipCPU project in that there is a variable delay from request to
// completion.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef MEMSIM_H
#define MEMSIM_H
 
class MEMSIM {
public:
typedef unsigned int BUSW;
typedef unsigned char uchar;
 
BUSW *m_mem, m_len, m_mask, m_head, m_tail, m_delay_mask, m_delay;
int *m_fifo_ack;
BUSW *m_fifo_data;
 
MEMSIM(const unsigned int nwords, const unsigned int delay=27);
~MEMSIM(void);
void load(const char *fname);
void load(const unsigned int addr, const char *buf,const size_t len);
void apply(const uchar wb_cyc, const uchar wb_stb,
const uchar wb_we,
const BUSW wb_addr, const BUSW wb_data,
const uchar wb_sel,
uchar &o_ack, uchar &o_stall, BUSW &o_data);
void operator()(const uchar wb_cyc, const uchar wb_stb,
const uchar wb_we,
const BUSW wb_addr, const BUSW wb_data,
const uchar wb_sel,
uchar &o_ack, uchar &o_stall, BUSW &o_data) {
apply(wb_cyc, wb_stb, wb_we, wb_addr, wb_data, wb_sel, o_ack, o_stall, o_data);
}
BUSW &operator[](const BUSW addr) { return m_mem[addr&m_mask]; }
};
 
#endif
/sim/verilated/oledsim.cpp
0,0 → 1,531
////////////////////////////////////////////////////////////////////////////////
//
// Filename: oledsim.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: The goal of this module is very specifically to simulate the
// PModOLEDrgb using a GTKMM controlled window. I'm doing this on
// an Linux computer with X-Windows, although one GTKMM selling point is
// that it should work in Windows as well. I won't vouch for that, as I
// haven't tested under windows.
//
// Either way, this controller only implements *some* of the OLED commands.
// There were just too many commands for me to be able to write them in the
// short order that I needed to get a test up and running. Therefore, this
// simulator will validate all commands and assure you they are valid
// commands, but it will only respond to some. For specifics, see the
// do_command() section below.
//
// You may notice a lot of assert() calls within this code. This is half
// the purpose of the code: to verify that interactions, when the take
// place, are valid. The sad problem and effect of this is simply that
// when bugs are present, the error/warning messages are not that complete.
// If you find yourself dealing with such an error, please feel free to
// explain the assert better before asserting, and then send your
// contributions back to me so that others can benefit from your work.
// (Don't you love the GPL?)
//
// 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 "oledsim.h"
 
const int OLEDSIM::OLED_HEIGHT = 64, OLEDSIM::OLED_WIDTH = 96;
 
const int MICROSECOND = 81,
tMINRESET = 3 * MICROSECOND, // 3 uS
tCYCLE = 13, // 150 * NANOSECOND, clock cycle time
tAS = 4, // 40 * NANOSECOND, address setup time
tAH = 4, // 40 * NANOSECOND, address hold time
tCSS = 7, // 75 * NANOSECOND, chip select setup
tCSH = 5, // 60 * NANOSECOND, chip select hold
tCLKL = 7, // 75 * NANOSECOND, time the clock must be low
tCLKH = 7; // 75 * NANOSECOND, time the clock must be high
 
void OLEDSIM::on_realize() {
Gtk::DrawingArea::on_realize();
 
// We'll be doing all of our drawing on an off-screen bit map. Here,
// let's allocate that pixel map ...
m_pix = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24,
OLED_WIDTH, OLED_HEIGHT);
 
// and a graphics context to be used when drawing to it.
m_gc = Cairo::Context::create(m_pix);
 
// We'll start the pixel map filled with all black, as this is what
// my device looks like when I'm not doing anything with it.
m_gc->set_source_rgb(0.0,0.0,0.0); // Black
m_gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
m_gc->fill();
}
 
void OLEDSIM::get_preferred_width_vfunc(int &min, int &nw) const {
// GTKMM wants to know how big we want our window to be.
// Let's request a window twice as big as we need, but insist that
// it never be smaller than one pixel output per one pixel input.
//
min = OLED_WIDTH;
nw = OLED_WIDTH * 2;
}
 
void OLEDSIM::get_preferred_height_vfunc(int &min, int &nw) const {
//
// Same thing as above, but this time for height, not width.
//
min = OLED_HEIGHT;
nw = OLED_HEIGHT * 2;
}
 
void OLEDSIM::get_preferred_width_for_height_vfunc(int h, int &min, int &nw) const {
min = OLED_WIDTH;
int k = (h+(OLED_HEIGHT/2))/OLED_HEIGHT;
if (k <= 0)
k = 1;
nw = OLED_WIDTH * k;
}
 
void OLEDSIM::get_preferred_height_for_width_vfunc(int w, int &min, int &nw) const {
min = OLED_HEIGHT;
int k = (w+(OLED_WIDTH/2))/OLED_WIDTH;
if (k <= 0)
k = 1;
nw = OLED_HEIGHT * k;
}
 
/*
* This is our simulation function. This is the function that gets called at
* every tick of our controller within Verilator. At each tick (and not twice
* per tick), the outputs are gathered and sent our way. Here, we just decode
* the power and reset outputs, and send everything else to handle_io().
*/
void OLEDSIM::operator()(const int iopwr, const int rstn, const int dpwr,
const int csn, const int sck, const int dcn, const int mosi) {
if (!iopwr) {
if (m_state != OLED_OFF) {
m_state = OLED_OFF;
clear_to(0.0);
queue_draw_area(0,0,get_width(), get_height());
}
assert(!dpwr);
} else if (!rstn) {
if (m_state != OLED_RESET) {
m_state = OLED_RESET;
m_locked = true;
clear_to(0.1);
m_reset_clocks = 0;
queue_draw_area(0,0,get_width(), get_height());
} if (m_reset_clocks < tMINRESET)
m_reset_clocks++;
assert(csn);
assert(sck);
} else if (dpwr) {
if (m_state != OLED_POWERED) {
m_state = OLED_POWERED;
queue_draw_area(0,0,get_width(), get_height());
if (!csn) {
printf("OLED-ERR: CSN=%d, SCK=%d, DCN=%d, MOSI=%d, from %d,%d,%d\n",
csn, sck, dcn, mosi,
m_last_csn, m_last_sck, m_last_dcn);
}
assert(csn); // Can't power up with SPI active.
}
 
handle_io(csn, sck, dcn, mosi);
} else {
if (m_state != OLED_VIO) {
m_state = OLED_VIO;
queue_draw_area(0,0,OLED_WIDTH, OLED_HEIGHT);
}
handle_io(csn, sck, dcn, mosi);
}
}
 
/* handle_io()
*
* We only enter this function if the I/O is powered up and the device is out
* of reset. The device may (or may not) be on. Our purpose here is to decode
* the SPI commands into a byte sequence, kept in m_data with a length given by
* m_idx. Once a command has completed, we call do_command() to actually
* process the values received, the arguments, etc. and do something with them.
*
*/
void OLEDSIM::handle_io(const int csn, const int sck, const int dcn, const int mosi) {
if ((csn != m_last_csn)||(sck != m_last_sck)||(dcn != m_last_dcn))
printf("OLED: HANDLE-IO(%d,%d,%d,%d) @[%d]%d\n",
csn, sck, dcn, mosi, m_idx, m_bitpos);
if (csn) {
// CSN is high when the chip isn't selected.
if (!m_last_csn) {
// If the chip was just selected, it then means that our
// command just completed. Let's process it here.
printf("OLED: Ending a command\n");
assert(m_idx > 0);
assert((m_bitpos&7)==0);
do_command(m_last_dcn, m_idx, m_data);
 
m_bitpos = 0;
m_idx = 0;
for(int i=0; i<8; i++)
m_data[i] = 0;
assert(m_last_sck);
} if (!sck)
printf("OLED: CSN = %d, SCK = %d, DCN = %d, MOSI = %d, from %d, %d, %d\n",
csn, sck, dcn, mosi,
m_last_csn, m_last_sck, m_last_dcn);
assert(sck);
m_bitpos = 0;
m_idx = 0;
} else {
if (m_last_csn) {
assert((sck)&&(m_last_sck));
assert(m_last_sck);
printf("OLED: Starting a command\n");
}
 
/*
if (m_last_dcn != dcn) {
m_address_counts = 0;
} m_address_counts++;
*/
 
if ((sck)&&(!m_last_sck)) {
m_bitpos++;
m_data[m_idx] = (m_data[m_idx]<<1)|mosi;
printf("OLED: Accepted bit: m_data[%d] = %02x\n",
m_idx, m_data[m_idx]);
if (m_bitpos >= 8) {
m_idx++;
m_bitpos &= 7;
}
assert(m_idx < 3+4+4);
// assert(m_address_count > tCSS);
} else if ((!sck)&&(m_last_sck)) {
}
}
 
m_last_csn = csn;
m_last_sck = sck;
m_last_dcn = dcn;
}
 
void OLEDSIM::do_command(const int dcn, const int len, char *data) {
assert(len > 0);
assert(len <= 11);
 
printf("OLED: RECEIVED CMD(%02x) ", data[0]&0x0ff);
if (len > 1) {
printf(" - ");
for(int i=1; i<len-1; i++)
printf("%02x:", data[i]&0x0ff);
printf("%02x", data[len-1]&0x0ff);
printf("\n");
}
if (dcn) {
// Do something with the pixmap
double dr, dg, db;
 
if (m_format == OLED_65kCLR) {
int r, g, b;
assert(len == 2);
r = (data[0]>>3)&0x01f;
g = ((data[0]<<3)&0x038)|((data[1]>>5)&0x07);
b = ((data[1] )&0x01f);
 
dr = r / 31.0;
dg = g / 63.0;
db = b / 31.0;
} else {
printf("OLED: UNSUPPORTED COLOR FORMAT!\n");
dr = dg = db = 0.0;
} set_gddram(m_col, m_row, dr, dg, db);
if (!m_vaddr_inc) {
m_col++;
if (m_col > m_col_end) {
m_col = m_col_start;
m_row++;
if (m_row > m_row_end)
m_row = m_row_start;
}
} else {
m_row++;
if (m_row > m_row_end) {
m_row = m_row_start;
m_col++;
if (m_col > m_col_end)
m_col = m_col_start;
}
}
} else if (m_locked) {
if ((len == 2)&&((data[0]&0x0ff) == 0x0fd)&&(data[1] == 0x12)) {
m_locked = false;
printf("OLED: COMMANDS UNLOCKED\n");
} else {
printf("OLED: COMMAND IGNORED, IC LOCKED\n");
}
} else {
// Command word
switch((data[0])&0x0ff) {
case 0x15: // Setup column start and end address
assert(len == 3);
assert((data[1]&0x0ff) <= 95);
assert((data[2]&0x0ff) <= 95);
m_col_start = data[1]&0x0ff;
m_col_end = data[2]&0x0ff;
assert(m_col_end >= m_col_start);
m_col = m_col_start;
break;
case 0x75: // Setup row start and end address
assert(len == 3);
assert((data[1]&0x0ff) <= 63);
assert((data[2]&0x0ff) <= 63);
assert(m_row_end >= m_row_start);
m_row_start = data[1]&0x0ff;
m_row_end = data[2]&0x0ff;
break;
case 0x81: // Set constrast for all color "A" segment
assert(len == 2);
break;
case 0x82: // Set constrast for all color "B" segment
assert(len == 2);
break;
case 0x83: // Set constrast for all color "C" segment
assert(len == 2);
break;
case 0x87: // Set master current attenuation factor
assert(len == 2);
break;
case 0x8a: // Set second pre-charge speed, color A
assert(len == 2);
break;
case 0x8b: // Set second pre-charge speed, color B
assert(len == 2);
break;
case 0x8c: // Set second pre-charge speed, color C
assert(len == 2);
break;
case 0xa0: // Set driver remap and color depth
assert(len == 2);
m_vaddr_inc = (data[1]&1)?true:false;
// m_fliplr = (data[1]&2)?true:false;
if ((data[1] & 0x0c0)==0)
m_format = OLED_256CLR;
else if ((data[1] & 0x0c0)==0x40)
m_format = OLED_65kCLR;
// else if ((data[1] & 0x0c0)==0x80)
// m_format = OLED_65kCLRTWO;
break;
case 0xa1: // Set display start line register by row
assert(len == 2);
break;
case 0xa2: // Set vertical offset by com
assert(len == 2);
break;
case 0xa4: // Set display mode
case 0xa5: // Fallthrough
case 0xa6: // Fallthrough
case 0xa7: // Fallthrough
assert(len == 1);
break;
case 0xa8: // Set multiplex ratio
assert(len == 2);
break;
case 0xab: // Dim Mode setting
assert(len == 6);
break;
case 0xad:
assert(len == 2);
assert((data[1]&0x0fe)==0x08e);
break;
case 0xac:
case 0xae:
case 0xaf:
assert(len == 1);
break;
case 0xb0: // Power save mode
assert((len == 2)&&((data[1] == 0x1a)||(data[1] == 0x0b)));
break;
case 0xb1: // Phase 1 and 2 period adjustment
assert(len == 2);
break;
case 0xb3: // Displaky clock divider/oscillator frequency
assert(len == 2);
break;
case 0xb8: // Set gray scale table
assert(0 && "Gray scale table not implemented");
break;
case 0xb9: // Enable Linear Gray Scale table
assert(len == 1);
break;
case 0xbb: // Set pre-charge level
assert(len == 2);
break;
case 0xbc: // NOP
case 0xbd: // NOP
assert(len == 1);
case 0xbe: // Set V_COMH
assert(len == 2);
break;
case 0xe3: // NOP
assert(len == 1);
break;
case 0xfd: // Set command lock
assert(len == 2);
if (data[1] == 0x16) {
m_locked = true;
printf("OLED: COMMANDS NOW LOCKED\n");
}
break;
case 0x21: // Draw Line
assert(len == 8);
break;
case 0x22: // Draw Rectangle
assert(len == 11);
break;
case 0x23: // Copy
assert(len == 7);
break;
case 0x24: // Dim Window
assert(len == 5);
break;
case 0x25: // Clear Window
assert(len == 5);
break;
case 0x26: // Fill Enable/Disable
assert(len == 2);
// if (data[0]&1)
// m_drect_fills = 1;
assert((data[1] & 0x10)==0);
break;
case 0x27: // Continuous horizontal and vertical scrolling setup
assert(len == 6);
break;
case 0x2e: // Deactivate scrolling
assert(len == 1);
// m_scrolling = false;
break;
case 0x2f: // Activate scrolling
assert(len == 1);
// m_scrolling = true;
break;
default:
printf("OLED: UNKNOWN COMMAND, data[0] = %02x\n", data[0] & 0x0ff);
assert(0);
break;
}
}
}
 
/*
* set_gddram()
*
* Set graphics display DRAM.
*
* Here is the heart of drawing on the device, or at least pixel level drawing.
* The device allows other types of drawing, such as filling rectangles and
* such. Here, we just handle the setting of pixels.
*
* You'll note that updates to the drawing area are only queued if the device
* is in powered mode.
*
* At some point, I may wish to implement scrolling. If/when that happens,
* the GDDRAM will not be affected, but the area that needs to be redrawn will
* be. Hence this routine will need to be adjusted at that time.
*/
void OLEDSIM::set_gddram(const int col, const int row,
const double dr, const double dg, const double db) {
// Set our color to that given by the rgb (double) parameters.
m_gc->set_source_rgb(dr, dg, db);
 
printf("OLED: Setting pixel[%2d,%2d]\n", col, row);
int drow; // dcol;
drow = row + m_display_start_row;
if (drow >= OLED_HEIGHT)
drow -= OLED_HEIGHT;
m_gc->rectangle(col, row, 1, 1);
m_gc->fill();
 
if (m_state == OLED_POWERED) {
// Need to adjust the invalidated area if scrolling is taking
// place.
double kw, kh;
kw = get_width()/(double)OLED_WIDTH;
kh = get_height()/(double)OLED_HEIGHT;
queue_draw_area(col*kw, row*kh, (int)(kw+0.5), (int)(kh+0.5));
}
}
 
/*
* clear_to()
*
* Clears the simulated device to a known grayscale value. Examples are
* 0.0 for black, or 0.1 for a gray that is nearly black. Note that this
* call does *not* invalidate our window. Perhaps it should, but for now that
* is the responsibility of whatever function calls this function.
*/
void OLEDSIM::clear_to(double v) {
// How do we apply this to our pixmap?
m_gc->set_source_rgb(v, v, v);
m_gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
m_gc->fill();
}
 
bool OLEDSIM::on_draw(CONTEXT &gc) {
gc->save();
if (m_state == OLED_POWERED) {
// Scrolling will be implemented here
gc->set_source(m_pix, 0, 0);
gc->scale(get_width()/(double)OLED_WIDTH,
get_height()/(double)OLED_HEIGHT);
gc->paint();
} else {
if ((m_state == OLED_VIO)||(m_state == OLED_RESET))
gc->set_source_rgb(0.1,0.1,0.1); // DARK gray
else
gc->set_source_rgb(0.0,0.0,0.0); // Black
// gc->rectangle(0, 0, OLED_WIDTH, OLED_HEIGHT);
gc->rectangle(0, 0, get_width(), get_height());
gc->fill();
} gc->restore();
 
return true;
}
 
OLEDWIN::OLEDWIN(void) {
m_sim = new OLEDSIM();
m_sim->set_size_request(OLEDSIM::OLED_WIDTH, OLEDSIM::OLED_HEIGHT);
set_border_width(0);
add(*m_sim);
show_all();
Gtk::Window::set_title(Glib::ustring("OLED Simulator"));
}
 
/sim/verilated/oledsim.h
0,0 → 1,128
////////////////////////////////////////////////////////////////////////////////
//
// Filename: oledsim.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: To simulate the interaction between the OLED board and my
// logic. This simulator tries to read from the SPI generated
// by the logic, verify that the SPI interaction is valid, and then
// draws the OLED memory to the screen so you can see how the OLED
// would work ... even without having an OLED connected.
//
// 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 OLEDSIM_H
#define OLEDSIM_H
 
#include <gtkmm.h>
#include <assert.h>
 
#define OLED_OFF 1
#define OLED_RESET 2
#define OLED_VIO 3
#define OLED_POWERED 4
#define OLED_65kCLR 0
#define OLED_256CLR 1
 
class OLEDSIM : public Gtk::DrawingArea {
public:
typedef Cairo::RefPtr<Cairo::Context> CAIROGC;
typedef const Cairo::RefPtr<Cairo::Context> CONTEXT;
typedef Cairo::RefPtr<Cairo::ImageSurface> CAIROIMG;
 
private:
CAIROIMG m_pix;
CAIROGC m_gc;
 
int m_state, m_reset_clocks; // , m_address_counts;
 
int m_last_csn, m_last_sck, m_last_dcn;
 
int m_idx, m_bitpos;
char m_data[16];
 
bool m_vaddr_inc, m_locked;
int m_format;
int m_col_start, m_col_end, m_col;
int m_row_start, m_row_end, m_row, m_display_start_row;
 
 
void do_command(const int dcn, const int len, char *data);
void handle_io(const int, const int, const int, const int);
void clear_to(const double v);
void set_gddram(const int, const int, const double, const double, const double);
public:
static const int OLED_HEIGHT, OLED_WIDTH;
 
OLEDSIM(void) : Gtk::DrawingArea() {
 
set_has_window(true);
Widget::set_can_focus(false);
set_size_request(OLED_WIDTH, OLED_HEIGHT);
 
m_state = OLED_OFF;
m_locked = true;
m_last_csn = 1;
m_last_sck = 1;
m_last_dcn = 1;
m_format = OLED_65kCLR;
m_display_start_row = 0;
m_vaddr_inc = false;
m_col = 0; m_row = 0;
m_col_start = 0; m_row_start = 0;
m_col_end = 95; m_row_end = 63;
}
 
void get_preferred_width_vfunc(int &min, int &nw) const;
void get_preferred_height_vfunc(int &min, int &nw) const;
void get_preferred_height_for_width_vfunc(int w, int &min, int &nw) const;
void get_preferred_width_for_height_vfunc(int h, int &min, int &nw) const;
 
virtual void on_realize();
virtual bool on_draw(CONTEXT &gc);
void operator()(const int iopwr, const int rstn, const int dpwr,
const int csn, const int sck, const int dcn, const int mosi);
};
 
class OLEDWIN : public Gtk::Window {
private:
OLEDSIM *m_sim;
 
public:
OLEDWIN(void);
~OLEDWIN(void) { delete m_sim; }
void operator()(int iopwr, int rstn, int dpwr,
int sck, int csn, int dcn, int mosi) {
(*m_sim)(iopwr, rstn, dpwr, sck, csn, dcn, mosi);
}
};
 
#endif
/sim/verilated/pipecmdr.h
0,0 → 1,238
////////////////////////////////////////////////////////////////////////////////
//
// Filename: pipecmdr.h
//
// Project: XuLA2-LX25 SoC based upon the ZipCPU
//
// Purpose: This program attaches to a Verilated Verilog IP core.
// It will not work apart from such a core. Once attached,
// it connects the simulated core to a controller via a TCP/IP pipe
// interface designed to act like a UART. Indeed, the final interface has
// often been a UART, although it is a JTAG-User command on the XULA board.
// Still, this provides simple test facility designed to verify that the
// IP core in question works prior to such actual hardware implementation,
// or alternatively to help debug a core after hardware implementation.
//
// 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 PIPECMDR_H
#define PIPECMDR_H
 
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <arpa/inet.h>
 
#include "testb.h"
 
#define PIPEBUFLEN 256
 
//
// UARTLEN (a macro)
//
// Attempt to approximate our responses to the number of ticks a UART command
// would respond.
//
// At 115200 Baud, 8 bits of data, no parity and one stop bit, there will
// bit ten bits per character and therefore 8681 clocks per transfer
// 8681 ~= 100 MHz / 115200 (bauds / second) * 10 bauds / character
//
// #define UARTLEN 8681 // Minimum ticks per character, 115200 Baud
//
// At 4MBaud, each bit takes 25 clocks. 10 bits would thus take 250 clocks
//
#define UARTLEN 732 // Ticks per character: 1MBaud, 81.25MHz clock
 
template <class VA> class PIPECMDR : public TESTB<VA> {
bool m_debug;
 
void setup_listener(const int port) {
struct sockaddr_in my_addr;
 
signal(SIGPIPE, SIG_IGN);
 
if (m_debug) printf("Listening on port %d\n", port);
 
m_skt = socket(AF_INET, SOCK_STREAM, 0);
if (m_skt < 0) {
perror("Could not allocate socket: ");
exit(-1);
}
 
// Set the reuse address option
{
int optv = 1, er;
er = setsockopt(m_skt, SOL_SOCKET, SO_REUSEADDR, &optv, sizeof(optv));
if (er != 0) {
perror("SockOpt Err:");
exit(-1);
}
}
 
memset(&my_addr, 0, sizeof(struct sockaddr_in)); // clear structure
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(port);
if (bind(m_skt, (struct sockaddr *)&my_addr, sizeof(my_addr))!=0) {
perror("BIND FAILED:");
exit(-1);
}
 
if (listen(m_skt, 1) != 0) {
perror("Listen failed:");
exit(-1);
}
}
 
public:
int m_skt, m_con;
char m_txbuf[PIPEBUFLEN], m_rxbuf[PIPEBUFLEN];
int m_ilen, m_rxpos, m_txpos, m_uart_wait, m_tx_busy;
bool m_started_flag;
bool m_copy;
 
PIPECMDR(const int port, const bool copy_to_stdout=true)
: TESTB<VA>(), m_copy(copy_to_stdout) {
m_debug = false;
m_con = m_skt = -1;
setup_listener(port);
m_rxpos = m_txpos = m_ilen = 0;
m_started_flag = false;
m_uart_wait = 0; // Flow control into the FPGA
m_tx_busy = 0; // Flow control out of the FPGA
}
 
virtual void kill(void) {
// Close any active connection
if (m_con >= 0) close(m_con);
if (m_skt >= 0) close(m_skt);
}
 
virtual void tick(void) {
if (m_con < 0) {
// Can we accept a connection?
struct pollfd pb;
 
pb.fd = m_skt;
pb.events = POLLIN;
poll(&pb, 1, 0);
 
if (pb.revents & POLLIN) {
m_con = accept(m_skt, 0, 0);
 
if (m_con < 0)
perror("Accept failed:");
}
}
 
TESTB<VA>::m_core->i_rx_stb = 0;
 
if (m_uart_wait == 0) {
if (m_ilen > 0) {
// Is there a byte in our buffer somewhere?
TESTB<VA>::m_core->i_rx_stb = 1;
TESTB<VA>::m_core->i_rx_data = m_rxbuf[m_rxpos++];
m_ilen--;
} else if (m_con > 0) {
// Is there a byte to be read here?
struct pollfd pb;
pb.fd = m_con;
pb.events = POLLIN;
if (poll(&pb, 1, 0) < 0)
perror("Polling error:");
if (pb.revents & POLLIN) {
if ((m_ilen =recv(m_con, m_rxbuf, sizeof(m_rxbuf), MSG_DONTWAIT)) > 0) {
m_rxbuf[m_ilen] = '\0';
if (m_rxbuf[m_ilen-1] == '\n') {
m_rxbuf[m_ilen-1] = '\0';
if (m_copy)
printf("< \'%s\'\n", m_rxbuf);
m_rxbuf[m_ilen-1] = '\n';
} else if (m_copy)
printf("< \'%s\'\n", m_rxbuf);
TESTB<VA>::m_core->i_rx_stb = 1;
TESTB<VA>::m_core->i_rx_data = m_rxbuf[0];
m_rxpos = 1; m_ilen--;
m_started_flag = true;
} else if (m_ilen < 0) {
// An error occurred, close the connection
// This could also be the
// indication of a simple
// connection close, so we deal
// with this quietly.
// perror("Read error: ");
// fprintf(stderr, "Closing connection\n");
close(m_con);
m_con = -1;
} else { // the connection closed on us
close(m_con);
m_con = -1;
}
}
} m_uart_wait = (TESTB<VA>::m_core->i_rx_stb)?UARTLEN:0;
} else {
// Still working on transmitting a character
m_uart_wait = m_uart_wait - 1;
}
 
TESTB<VA>::tick();
 
if (m_tx_busy == 0) {
if ((TESTB<VA>::m_core->o_tx_stb)&&(m_con > 0)) {
m_txbuf[m_txpos++] = TESTB<VA>::m_core->o_tx_data;
if ((TESTB<VA>::m_core->o_tx_data == '\n')||(m_txpos >= (int)sizeof(m_txbuf))) {
int snt = 0;
snt = send(m_con, m_txbuf, m_txpos, 0);
if (snt < 0) {
close(m_con);
m_con = -1;
snt = 0;
}
m_txbuf[m_txpos] = '\0';
if (m_copy) printf("> %s", m_txbuf);
if (snt < m_txpos) {
fprintf(stderr, "Only sent %d bytes of %d!\n",
snt, m_txpos);
}
m_txpos = 0;
}
}
} else
m_tx_busy--;
 
if ((TESTB<VA>::m_core->o_tx_stb)&&(TESTB<VA>::m_core->i_tx_busy==0))
m_tx_busy = UARTLEN;
TESTB<VA>::m_core->i_tx_busy = (m_tx_busy != 0);
}
};
 
#endif
/sim/verilated/port.h
0,0 → 1,52
////////////////////////////////////////////////////////////////////////////////
//
// Filename: port.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
// 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 PORT_H
#define PORT_H
 
// #define FPGAHOST "lazarus"
#define FPGAHOST "localhost"
#define FPGATTY "/dev/ttyUSB1"
#define FPGAPORT 6510
 
#ifndef FORCE_UART
#define FPGAOPEN(V) V= new FPGA(new NETCOMMS(FPGAHOST, FPGAPORT))
#else
#define FPGAOPEN(V) V= new FPGA(new TTYCOMMS(FPGATTY))
#endif
 
#endif
/sim/verilated/sdspisim.cpp
0,0 → 1,516
///////////////////////////////////////////////////////////////////////////
//
//
// Filename: sdspisim.cpp
//
// Project: Wishbone Controlled SD-Card Controller over SPI port
//
// Purpose: This library simulates the operation of a SPI commanded SD-Card,
// such as might be found on a XuLA2-LX25 board made by xess.com.
//
// This simulator is for testing use in a Verilator/C++ environment, where
// it would be used in place of the actual hardware.
//
// Creator: Dan Gisselquist
// 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 "sdspisim.h"
 
static const unsigned
MICROSECONDS = 80, // Clocks in a microsecond
MILLISECONDS = MICROSECONDS * 1000,
tRESET = 4*MILLISECONDS; // Just a wild guess
/*
static const unsigned DEVID = 0x0115,
DEVESD = 0x014,
MICROSECONDS = 100,
MILLISECONDS = MICROSECONDS * 1000,
SECONDS = MILLISECONDS * 1000,
tW = 50 * MICROSECONDS, // write config cycle time
tBE = 32 * SECONDS,
tDP = 10 * SECONDS,
tRES = 30 * SECONDS,
// Shall we artificially speed up this process?
tPP = 12 * MICROSECONDS,
tSE = 15 * MILLISECONDS;
// or keep it at the original speed
// tPP = 1200 * MICROSECONDS,
// tSE = 1500 * MILLISECONDS;
*/
static const unsigned
CCS = 1; // 0: SDSC card, 1: SDHC or SDXC card
 
SDSPISIM::SDSPISIM(const bool debug) {
m_dev = NULL;
m_last_sck = 1;
m_block_address = (CCS==1);
m_host_supports_high_capacity = false;
m_powerup_busy = -1;
m_reset_state = SDSPI_POWERUP_RESET;
//
m_csd[ 0] = 0; // Normal SDcard, not high capacity
m_csd[ 1] = 0x0f;
m_csd[ 2] = 0x0f;
m_csd[ 3] = 0x32; // Can be either 0x32 (25MHz) or 0x5a (50MHz)
m_csd[ 4] = 0x5b; // Could also be 0x07b, if we supported more comands(?)
m_csd[ 5] = 0x59; // 9-> 2^9,or 512 bytes (10->1024, 11->2048, no othrs)
m_csd[ 6] = 0x00; // partial blocks allowed?
m_csd[ 7] = 0x00; // C_SIZE, 2'b00, then top 6 bits
m_csd[ 8] = 0; // C_SIZE, 22-bits, mid 8 bits
m_csd[ 9] = 0; // C_SIZE, 22-bits, bottom 8 bits
m_csd[10] = 0x7f;
m_csd[11] = 0x80;
m_csd[12] = 0x0a;
m_csd[13] = 0x40;
m_csd[14] = 0; // R/W: file format, copy, write protect, etc.
m_csd[15] = cmdcrc(15, m_csd);
 
// CID Register
m_cid[ 0] = 0xba;
m_cid[ 1] = 0xd0;
m_cid[ 2] = 0xda;
m_cid[ 3] = 0xdd;
m_cid[ 4] = 0;
m_cid[ 5] = 0xde;
m_cid[ 6] = 0xad;
m_cid[ 7] = 0xbe;
m_cid[ 8] = 0xef;
m_cid[ 9] = 0x20;
m_cid[10] = 0x16;
m_cid[11] = 0x05;
m_cid[12] = 0x26;
m_cid[13] = 0;
m_cid[14] = 0;
m_cid[15] = cmdcrc(15, m_cid);
 
// m_write_count = 0;
// m_ireg = m_oreg = 0;
// m_sreg = 0x01c;
// m_creg = 0x001; // Iinitial creg on delivery
 
//
m_reading_data = false;
m_have_token = false;
m_debug = debug;
}
 
void SDSPISIM::load(const char *fname) {
m_dev = fopen(fname, "r+b");
 
if (m_dev) {
unsigned long devln;
fseek(m_dev, 0l, SEEK_END);
devln = ftell(m_dev);
fseek(m_dev, 0l, SEEK_SET);
 
m_devblocks = devln>>9;
 
if (m_debug) printf("SDCARD: NBLOCKS = %ld\n", m_devblocks);
}
}
 
int SDSPISIM::operator()(const int csn, const int sck, const int mosi) {
// Keep track of a timer to determine when page program and erase
// cycles complete.
 
/*
if (m_write_count > 0) {
//
}
*/
 
m_delay++;
if (m_powerup_busy>0)
m_powerup_busy--;
 
if (csn) {
m_delay = 0;
m_cmdidx= 0;
m_rspidx= 0;
m_bitpos= 0;
m_delay = 0;
m_busy = false;
m_last_sck = sck;
m_syncd = false;
m_last_miso = 1;
m_dat_out = 0x0ff;
// Reset everything when not selected
return 0;
} else if (sck == m_last_sck) {
m_last_sck = sck;
return m_last_miso;
} else if (!m_last_sck) {
// Register our input on the rising edge
m_mosi = mosi;
m_syncd= true;
m_last_sck = sck;
return m_last_miso;
} if (!m_syncd) {
m_last_sck = sck;
return m_last_miso;
}
 
// Only change our output on the falling edge
 
m_last_sck = sck;
if (m_debug) printf("SDSPI: (%3d) [%d,%d,%d] ", m_delay, csn, sck, m_mosi);
// assert(m_delay > 20);
 
m_bitpos++;
m_dat_in = (m_dat_in<<1)|m_mosi;
 
 
if (m_debug) printf("(bitpos=%d,dat_in=%02x)\n", m_bitpos&7, m_dat_in&0x0ff);
 
if ((m_bitpos&7)==0) {
if (m_debug) printf("SDSPI--RX BYTE %02x\n", m_dat_in&0x0ff);
m_dat_out = 0xff;
if (m_reading_data) {
if (m_have_token) {
m_block_buf[m_rxloc++] = m_dat_in;
if (m_debug) printf("SDSPI: WR[%3d] = %02x\n", m_rxloc-1,
m_dat_in&0x0ff);
if (m_rxloc >= 512+2) {
unsigned crc, rxcrc;
crc = blockcrc(512, m_block_buf);
rxcrc = ((m_block_buf[512]&0x0ff)<<8)
|(m_block_buf[513]&0x0ff);
 
if (m_debug) printf("LEN = %d\n", m_rxloc);
if (m_debug) printf("CHECKING CRC: (rx) %04x =? %04x (calc)\n",
crc, rxcrc);
m_reading_data = false;
m_have_token = false;
if (rxcrc == crc)
m_dat_out = 5;
else {
m_dat_out = 0x0b;
assert(rxcrc == crc);
}
}
} else {
if ((m_dat_in&0x0ff) == 0x0fe) {
if (m_debug) printf("SDSPI: TOKEN!!\n");
m_have_token = true;
m_rxloc = 0;
} else if (m_debug)
printf("SDSPI: waiting on token\n");
}
} else if (m_cmdidx < 6) {
if (m_debug) printf("SDSPI: CMDIDX = %d\n", m_cmdidx);
// All commands *must* start with a 01... pair of bits.
if (m_cmdidx == 0)
assert((m_dat_in&0xc0)==0x40);
 
// Record the command for later processing
m_cmdbuf[m_cmdidx++] = m_dat_in;
} else if (m_cmdidx == 6) {
// We're going to start a response from here ...
m_rspidx = 0;
m_blkdly = 0;
m_blkidx = SDSPI_MAXBLKLEN;
if (m_debug) {
printf("SDSPI: CMDIDX = %d -- WE HAVE A COMMAND! [ ", m_cmdidx);
for(int i=0; i<6; i++)
printf("%02x ", m_cmdbuf[i] & 0xff);
printf("]\n"); fflush(stdout);
}
 
unsigned arg;
arg = ((((((m_cmdbuf[1]<<8)|(m_cmdbuf[2]&0x0ff))<<8)
|(m_cmdbuf[3]&0x0ff))<<8)
|(m_cmdbuf[4]&0x0ff));
arg &= 0x0ffffffff;
 
// Check the CRC
if (!check_cmdcrc(m_cmdbuf)) {
assert(0 && "BAD CRC");
m_rspbuf[0] = 0x09;
m_rspdly = 1;
} else if (m_altcmd_flag) {
switch(m_cmdbuf[0]&0x03f) {
case 41: // ACMD41 -- SD_SEND_OP_COND
// and start initialization sequence
assert((m_reset_state == SDSPI_RCVD_CMD8)||(m_reset_state == SDSPI_RCVD_ACMD41)||(m_reset_state == SDSPI_RESET_COMPLETE));
if((unsigned)m_powerup_busy>tRESET)
m_powerup_busy = tRESET;
assert((arg&0x0bfffffff) == 0);
m_rspbuf[0] = (m_powerup_busy)?1:0;
m_rspdly = 2;
m_host_supports_high_capacity = (m_cmdbuf[1]&0x40)?1:0;
m_reset_state = (m_powerup_busy)?
SDSPI_RCVD_ACMD41
:SDSPI_RESET_COMPLETE;
break;
case 51: // ACMD51
m_block_buf[0] = 0x0fe;
for(int j=0; j<8; j++)
m_block_buf[j+1] = m_csd[j];
m_blklen = 8;
add_block_crc(m_blklen, m_block_buf);
 
m_blkdly = 0;
m_blkidx = 0;
m_dat_out = 0;
break;
case 13: // ACMD13
case 22: // ACMD22
case 23: // ACMD23
case 42: // ACMD42
default: // Unimplemented command!
m_rspbuf[0] = 0x04;
m_rspdly = 4;
fprintf(stderr, "SDSPI ERR: Alt command ACMD%d not implemented!\n", m_cmdbuf[0]&0x03f);
assert(0 && "Not Implemented");
} m_altcmd_flag = false;
} else {
m_altcmd_flag = false;
memset(m_rspbuf, 0x0ff, SDSPI_RSPLEN);
if (m_debug) printf("SDSPI: Received a command 0x%02x (%d)\n",
m_cmdbuf[0], m_cmdbuf[0]&0x03f);
switch(m_cmdbuf[0]&0x3f) {
case 0: // CMD0 -- GO_IDLE_STATE
m_rspbuf[0] = 0x01;
m_rspdly = 4;
m_reset_state = SDSPI_CMD0_IDLE;
break;
case 1: // CMD1 -- SEND_OP_COND
assert((arg&0x0bfffffff) == 0);
m_rspbuf[0] = 0x02;
m_rspdly = 4;
m_host_supports_high_capacity = (m_cmdbuf[1]&0x40)?1:0;
break;
case 8: // CMD8 -- SEND_IF_COND
assert((arg&0x0fffff000) == 0);
m_rspbuf[0] = 0x00;
// See p82 for this format
m_rspbuf[1] = 0;
m_rspbuf[2] = 0;
// If we do not accept the voltage range
m_rspbuf[3] = 0;
// Now, check if we accept it
// We only accept 2.7-3.6V in this
// simulation.
if ((arg&0x0f00)==0x0100)
m_rspbuf[3] = 1;
m_rspbuf[4] = (char)(arg&0x0ff);
m_rspdly = 4;
assert((m_reset_state == SDSPI_CMD0_IDLE)||(m_reset_state == SDSPI_RCVD_CMD8));
m_reset_state = SDSPI_RCVD_CMD8;
break;
case 9: // CMD9 -- SEND_CSD
// Block read, returning start token,
// 16 bytes, then 2 crc bytes
assert(m_reset_state == SDSPI_IN_OPERATION);
m_rspbuf[0] = 0x00;
memset(m_block_buf, 0x0ff, SDSPI_MAXBLKLEN);
m_block_buf[0] = 0x0fe;
for(int j=0; j<16; j++)
m_block_buf[j+1] = m_csd[j];
m_blklen = 16;
add_block_crc(m_blklen, m_block_buf);
 
m_blkdly = 60;
m_blkidx = 0;
break;
case 10: // CMD10 -- SEND_CID
// Block read, returning start token,
// 16 bytes, then 2 crc bytes
assert(m_reset_state == SDSPI_IN_OPERATION);
m_rspbuf[0] = 0x00;
memset(m_block_buf, 0x0ff, SDSPI_MAXBLKLEN);
m_block_buf[0] = 0x0fe;
for(int j=0; j<16; j++)
m_block_buf[j+1] = m_cid[j];
m_blklen = 16;
add_block_crc(m_blklen, m_block_buf);
 
m_blkdly = 60;
m_blkidx = 0;
break;
case 13: // CMD13 -- SEND_STATUS
assert(m_reset_state == SDSPI_IN_OPERATION);
m_rspbuf[0] = 0x00;
m_rspbuf[1] = 0x00;
// if (m_wp_fault) m_rspbuf[1]|=0x20;
// if (m_err) m_rspbuf[1]|=0x04;
m_rspdly = 4;
break;
case 17: // CMD17 -- READ_SINGLE_BLOCK
assert(m_reset_state == SDSPI_IN_OPERATION);
m_rspbuf[0] = 0x00;
memset(m_block_buf, 0x0ff, SDSPI_MAXBLKLEN);
if (m_dev) {
if (m_debug) printf("Reading from block %08x of %08lx\n", arg, m_devblocks);
if (m_block_address) {
assert(arg < m_devblocks);
fseek(m_dev, arg<<9, SEEK_SET);
} else {
assert(arg < m_devblocks<<9);
fseek(m_dev, arg, SEEK_SET);
}
} m_block_buf[0] = 0x0fe;
m_blklen = 512; // (1<<m_csd[5]);
if (m_dev)
m_blklen = fread(&m_block_buf[1], m_blklen, 1, m_dev);
else
memset(&m_block_buf[1], 0, m_blklen);
m_blklen = (m_blklen != 512) ? 512 : m_blklen;
add_block_crc(m_blklen, m_block_buf);
 
m_blkdly = 60;
m_blkidx = 0;
break;
case 24: // CMD24 -- WRITE_BLOCK
m_reading_data = true;
m_have_token = false;
m_dat_out = 0;
break;
case 55: // CMD55 -- APP_CMD
m_rspbuf[0] = 0x00;
m_rspdly = 2;
m_altcmd_flag = true;
break;
case 58: // CMD58 -- READ_OCR, respond R7
// argument is stuff bits/dont care
m_rspbuf[0] = 0x00;
// See p112, Tbl 5-1 for this format
m_rspbuf[1] = ((m_powerup_busy)?0x80:0)
|(CCS?0x40:0);
m_rspbuf[2] = 0xff;// 2.7-3.6V supported
m_rspbuf[3] = 0x80;
m_rspbuf[4] = 0; // No low-voltage supt
m_rspdly = 4;
 
if (m_reset_state == SDSPI_RESET_COMPLETE)
m_reset_state = SDSPI_IN_OPERATION;
break;
case 6: // CMD6 -- SWITCH_FUNC
case 12: // CMD12 -- STOP_TRANSMISSION (!impl)
case 16: // CMD16 -- SET_BLOCKLEN
case 18: // CMD18 -- READ_MULTIPLE_BLOCK
case 25: // CMD25 -- WRITE_MULTIPLE_BLOCK
case 27: // CMD27 -- PROGRAM_CSD
case 32: // CMD32 -- ERASE_WR_BLK_START_ADDR
case 33: // CMD33 -- ERASE_WR_BLK_END_ADDR
case 38: // CMD38 -- ERASE
case 56: // CMD56 -- GEN_CMD
default: // Unimplemented command
m_rspbuf[0] = 0x04;
m_rspdly = 4;
if (m_debug) printf("SDSPI ERR: Command CMD%d not implemented!\n", m_cmdbuf[0]&0x03f);
fflush(stdout);
assert(0 && "Not Implemented");
}
} m_cmdidx++;
 
// If we are using blocks, add bytes for the start
// token and the two CRC bytes
m_blklen += 3;
} else if (m_rspdly > 0) {
assert((m_dat_in&0x0ff) == 0x0ff);
// A delay until a response is given
if (m_busy)
m_dat_out = 0;
m_rspdly--;
} else if (m_rspidx < SDSPI_RSPLEN) {
assert((m_dat_in&0x0ff) == 0x0ff);
m_dat_out = m_rspbuf[m_rspidx++];
} else if (m_blkdly > 0) {
assert((m_dat_in&0x0ff) == 0x0ff);
m_blkdly--;
} else if (m_blkidx < SDSPI_MAXBLKLEN) {
assert((m_dat_in&0x0ff) == 0x0ff);
m_dat_out = m_block_buf[m_blkidx++];
}
// else m_dat_out = 0x0ff; // So set already above
}
 
int result = (m_dat_out&0x80)?1:0;
m_dat_out <<= 1;
m_delay = 0;
m_last_miso = result;
fflush(stdout);
return result;
}
 
unsigned SDSPISIM::cmdcrc(int len, char *buf) const {
unsigned int fill = 0, taps = 0x12;
 
for(int i=0; i<len; i++) {
fill ^= buf[i];
for(int j=0; j<8; j++) {
if (fill&0x80)
fill = (fill<<1)^taps;
else
fill <<= 1;
}
}
 
fill &= 0x0fe; fill |= 1;
return fill;
}
 
bool SDSPISIM::check_cmdcrc(char *buf) const {
unsigned fill = cmdcrc(5, buf);
if (m_debug) printf("SDSPI: CRC-CHECK, should have a CRC of %02x\n", fill);
return (fill == (buf[5]&0x0ff));
}
 
unsigned SDSPISIM::blockcrc(int len, char *buf) const {
unsigned int fill = 0, taps = 0x1021;
bool dbg = (len == 512);
 
for(int i=0; i<len; i++) {
if (dbg) { printf("BUF[%3d] = %02x\n", i, buf[i]&0x0ff); }
fill ^= ((buf[i]&0x0ff) << 8);
for(int j=0; j<8; j++) {
if (fill&0x8000)
fill = (fill<<1)^taps;
else
fill <<= 1;
}
}
 
fill &= 0x0ffff;
if (dbg) { printf("BLOCKCRC(%d,...) = %04x\n", len, fill); }
return fill;
}
 
void SDSPISIM::add_block_crc(int len, char *buf) const {
unsigned fill = blockcrc(len, &buf[1]);
 
buf[len+1] = (fill >> 8)&0x0ff;
buf[len+2] = (fill )&0x0ff;
}
 
/sim/verilated/sdspisim.h
0,0 → 1,91
////////////////////////////////////////////////////////////////////////////////
//
// Filename: sdspisim.h
//
// Project: Wishbone Controlled SD-Card Controller over SPI port
//
// Purpose: This library simulates the operation of a SPI commanded SD-Card,
// such as might be found on a XuLA2-LX25 board made by xess.com.
//
// This simulator is for testing use in a Verilator/C++ environment, where
// it would 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef SDSPISIM_H
#define SDSPISIM_H
 
#include <stdio.h>
 
typedef enum eRESET_STATES {
SDSPI_POWERUP_RESET,
SDSPI_CMD0_IDLE,
SDSPI_RCVD_CMD8,
SDSPI_RCVD_ACMD41,
SDSPI_RESET_COMPLETE,
SDSPI_IN_OPERATION
} RESET_STATES;
 
#define SDSPI_RSPLEN 8
#define SDSPI_MAXBLKLEN (1+2048+2)
#define SDSPI_CSDLEN (16)
#define SDSPI_CIDLEN (16)
class SDSPISIM {
FILE *m_dev;
unsigned long m_devblocks;
 
int m_last_sck, m_delay, m_mosi;
bool m_busy, m_debug, m_block_address, m_altcmd_flag,
m_syncd, m_host_supports_high_capacity, m_reading_data,
m_have_token;
 
RESET_STATES m_reset_state;
 
int m_cmdidx, m_bitpos, m_rspidx, m_rspdly, m_blkdly,
m_blklen, m_blkidx, m_last_miso, m_powerup_busy,
m_rxloc;
char m_cmdbuf[8], m_dat_out, m_dat_in;
char m_rspbuf[SDSPI_RSPLEN];
char m_block_buf[SDSPI_MAXBLKLEN];
char m_csd[SDSPI_CSDLEN], m_cid[SDSPI_CIDLEN];
 
public:
SDSPISIM(const bool debug = false);
void load(const char *fname);
void debug(const bool dbg) { m_debug = dbg; }
bool debug(void) const { return m_debug; }
int operator()(const int csn, const int sck, const int dat);
unsigned cmdcrc(int ln, char *buf) const;
bool check_cmdcrc(char *buf) const;
unsigned blockcrc(int ln, char *buf) const;
void add_block_crc(int ln, char *buf) const;
};
 
#endif
/sim/verilated/testb.h
0,0 → 1,106
////////////////////////////////////////////////////////////////////////////////
//
// Filename: testb.h
//
// Project: Zip CPU -- a small, lightweight, RISC CPU core
//
// Purpose: A wrapper for a common interface to a clocked FPGA core
// begin exercised in Verilator.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015,2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
#ifndef TESTB_H
#define TESTB_H
 
#include <stdio.h>
#include <stdint.h>
#include <verilated_vcd_c.h>
 
template <class VA> class TESTB {
public:
VA *m_core;
VerilatedVcdC* m_trace;
unsigned long m_tickcount;
 
TESTB(void) : m_trace(NULL), m_tickcount(0l) {
m_core = new VA;
Verilated::traceEverOn(true);
}
virtual ~TESTB(void) {
if (m_trace) m_trace->close();
delete m_core;
m_core = NULL;
}
 
virtual void opentrace(const char *vcdname) {
if (!m_trace) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(vcdname);
}
}
 
virtual void closetrace(void) {
if (m_trace) {
m_trace->close();
m_trace = NULL;
}
}
 
virtual void eval(void) {
m_core->eval();
}
 
virtual void tick(void) {
m_tickcount++;
 
// Make sure we have our evaluations straight before the top
// of the clock. This is necessary since some of the
// connection modules may have made changes, for which some
// logic depends. This forces that logic to be recalculated
// before the top of the clock.
eval();
if (m_trace) m_trace->dump(10*m_tickcount-2);
m_core->i_clk = 1;
eval();
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk = 0;
eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
}
 
virtual void reset(void) {
m_core->i_rst = 1;
tick();
m_core->i_rst = 0;
// printf("RESET\n");
}
};
 
#endif
/sim/verilated/uartsim.cpp
0,0 → 1,349
////////////////////////////////////////////////////////////////////////////////
//
// Filename: uartsim.cpp
//
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: To forward a Verilator simulated UART link over a TCP/IP pipe.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <ctype.h>
 
#include "uartsim.h"
 
void UARTSIM::setup_listener(const int port) {
struct sockaddr_in my_addr;
 
signal(SIGPIPE, SIG_IGN);
 
printf("Listening on port %d\n", port);
 
m_skt = socket(AF_INET, SOCK_STREAM, 0);
if (m_skt < 0) {
perror("Could not allocate socket: ");
exit(-1);
}
 
// Set the reuse address option
{
int optv = 1, er;
er = setsockopt(m_skt, SOL_SOCKET, SO_REUSEADDR, &optv, sizeof(optv));
if (er != 0) {
perror("SockOpt Err:");
exit(-1);
}
}
 
memset(&my_addr, 0, sizeof(struct sockaddr_in)); // clear structure
my_addr.sin_family = AF_INET;
// Use *all* internet ports to this computer, allowing connections from
// any/every one of them.
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(port);
if (bind(m_skt, (struct sockaddr *)&my_addr, sizeof(my_addr))!=0) {
perror("BIND FAILED:");
exit(-1);
}
 
if (listen(m_skt, 1) != 0) {
perror("Listen failed:");
exit(-1);
}
}
 
UARTSIM::UARTSIM(const int port) {
m_conrd = m_conwr = m_skt = -1;
if (port == 0) {
m_conrd = STDIN_FILENO;
m_conwr = STDOUT_FILENO;
} else
setup_listener(port);
setup(25); // Set us up for (default) 8N1 w/ a baud rate of CLK/25
m_rx_baudcounter = 0;
m_tx_baudcounter = 0;
m_rx_state = RXIDLE;
m_tx_state = TXIDLE;
}
 
void UARTSIM::kill(void) {
fflush(stdout);
 
// Quickly double check that we aren't about to close stdin/stdout
if (m_conrd == STDIN_FILENO)
m_conwr = -1;
if (m_conwr == STDOUT_FILENO)
m_conwr = -1;
// Close any active connection
if (m_conrd >= 0) close(m_conrd);
if ((m_conwr >= 0)&&(m_conwr != m_conrd)) close(m_conwr);
if (m_skt >= 0) close(m_skt);
 
m_conrd = m_conwr = m_skt = -1;
}
 
void UARTSIM::setup(unsigned isetup) {
if (isetup != m_setup) {
m_setup = isetup;
m_baud_counts = (isetup & 0x0ffffff);
m_nbits = 8-((isetup >> 28)&0x03);
m_nstop =((isetup >> 27)&1)+1;
m_nparity = (isetup >> 26)&1;
m_fixdp = (isetup >> 25)&1;
m_evenp = (isetup >> 24)&1;
}
}
 
int UARTSIM::nettick(int i_tx) {
int o_rx = 1;
 
if ((m_conrd < 0)&&(m_conwr<0)&&(m_skt>=0)) {
// Can we accept a connection?
struct pollfd pb;
 
pb.fd = m_skt;
pb.events = POLLIN;
poll(&pb, 1, 0);
 
if (pb.revents & POLLIN) {
m_conrd = accept(m_skt, 0, 0);
m_conwr = m_conrd;
 
if (m_conrd < 0)
perror("Accept failed:");
}
}
 
if ((!i_tx)&&(m_last_tx))
m_rx_changectr = 0;
else m_rx_changectr++;
m_last_tx = i_tx;
 
if (m_rx_state == RXIDLE) {
if (!i_tx) {
m_rx_state = RXDATA;
m_rx_baudcounter =m_baud_counts+m_baud_counts/2-1;
m_rx_baudcounter -= m_rx_changectr;
m_rx_busy = 0;
m_rx_data = 0;
}
} else if (m_rx_baudcounter <= 0) {
if (m_rx_busy >= (1<<(m_nbits+m_nparity+m_nstop-1))) {
m_rx_state = RXIDLE;
if (m_conwr >= 0) {
char buf[1];
buf[0] = (m_rx_data >> (32-m_nbits-m_nstop-m_nparity))&0x0ff;
if (1 != send(m_conwr, buf, 1, 0)) {
close(m_conwr);
m_conrd = m_conwr = -1;
}
}
} else {
m_rx_busy = (m_rx_busy << 1)|1;
// Low order bit is transmitted first, in this
// order:
// Start bit (1'b1)
// bit 0
// bit 1
// bit 2
// ...
// bit N-1
// (possible parity bit)
// stop bit
// (possible secondary stop bit)
m_rx_data = ((i_tx&1)<<31) | (m_rx_data>>1);
} m_rx_baudcounter = m_baud_counts-1;
} else
m_rx_baudcounter--;
 
if (m_tx_state == TXIDLE) {
struct pollfd pb;
pb.fd = m_conrd;
pb.events = POLLIN;
if (poll(&pb, 1, 0) < 0)
perror("Polling error:");
if (pb.revents & POLLIN) {
char buf[1];
if (1 == recv(m_conrd, buf, 1, MSG_DONTWAIT)) {
m_tx_data = (-1<<(m_nbits+m_nparity+1))
// << nstart_bits
|((buf[0]<<1)&0x01fe);
if (m_nparity) {
int p;
 
// If m_nparity is set, we need to then
// create the parity bit.
if (m_fixdp)
p = m_evenp;
else {
p = (m_tx_data >> 1)&0x0ff;
p = p ^ (p>>4);
p = p ^ (p>>2);
p = p ^ (p>>1);
p &= 1;
p ^= m_evenp;
}
m_tx_data |= (p<<(m_nbits+m_nparity));
}
m_tx_busy = (1<<(m_nbits+m_nparity+m_nstop+1))-1;
m_tx_state = TXDATA;
o_rx = 0;
m_tx_baudcounter = m_baud_counts-1;
}
}
} else if (m_tx_baudcounter <= 0) {
m_tx_data >>= 1;
m_tx_busy >>= 1;
if (!m_tx_busy)
m_tx_state = TXIDLE;
else
m_tx_baudcounter = m_baud_counts-1;
o_rx = m_tx_data&1;
} else {
m_tx_baudcounter--;
o_rx = m_tx_data&1;
}
 
return o_rx;
}
 
int UARTSIM::fdtick(int i_tx) {
int o_rx = 1;
 
if ((!i_tx)&&(m_last_tx))
m_rx_changectr = 0;
else m_rx_changectr++;
m_last_tx = i_tx;
 
if (m_rx_state == RXIDLE) {
if (!i_tx) {
m_rx_state = RXDATA;
m_rx_baudcounter =m_baud_counts+m_baud_counts/2-1;
m_rx_baudcounter -= m_rx_changectr;
m_rx_busy = 0;
m_rx_data = 0;
}
} else if (m_rx_baudcounter <= 0) {
if (m_rx_busy >= (1<<(m_nbits+m_nparity+m_nstop-1))) {
m_rx_state = RXIDLE;
if (m_conwr >= 0) {
char buf[1];
buf[0] = (m_rx_data >> (32-m_nbits-m_nstop-m_nparity))&0x0ff;
if (1 != write(m_conwr, buf, 1)) {
fprintf(stderr, "ERR while attempting to write out--closing output port\n");
perror("UARTSIM::write() ");
m_conrd = m_conwr = -1;
}
}
} else {
m_rx_busy = (m_rx_busy << 1)|1;
// Low order bit is transmitted first, in this
// order:
// Start bit (1'b1)
// bit 0
// bit 1
// bit 2
// ...
// bit N-1
// (possible parity bit)
// stop bit
// (possible secondary stop bit)
m_rx_data = ((i_tx&1)<<31) | (m_rx_data>>1);
} m_rx_baudcounter = m_baud_counts-1;
} else
m_rx_baudcounter--;
 
if ((m_tx_state == TXIDLE)&&(m_conrd >= 0)) {
struct pollfd pb;
pb.fd = m_conrd;
pb.events = POLLIN;
if (poll(&pb, 1, 0) < 0)
perror("Polling error:");
if (pb.revents & POLLIN) {
char buf[1];
int nr;
if (1==(nr = read(m_conrd, buf, 1))) {
m_tx_data = (-1<<(m_nbits+m_nparity+1))
// << nstart_bits
|((buf[0]<<1)&0x01fe);
if (m_nparity) {
int p;
 
// If m_nparity is set, we need to then
// create the parity bit.
if (m_fixdp)
p = m_evenp;
else {
p = (m_tx_data >> 1)&0x0ff;
p = p ^ (p>>4);
p = p ^ (p>>2);
p = p ^ (p>>1);
p &= 1;
p ^= m_evenp;
}
m_tx_data |= (p<<(m_nbits+m_nparity));
}
m_tx_busy = (1<<(m_nbits+m_nparity+m_nstop+1))-1;
m_tx_state = TXDATA;
o_rx = 0;
m_tx_baudcounter = m_baud_counts-1;
} else if (nr < 0) {
fprintf(stderr, "ERR while attempting to read in--closing input port\n");
perror("UARTSIM::read() ");
m_conrd = -1;
} // and we really don't care if nr == 0 except that
// the poll above is supposed to keep it from happening
}
} else if (m_tx_baudcounter == 0) {
m_tx_data >>= 1;
m_tx_busy >>= 1;
if (!m_tx_busy)
m_tx_state = TXIDLE;
else
m_tx_baudcounter = m_baud_counts-1;
o_rx = m_tx_data&1;
} else {
m_tx_baudcounter--;
o_rx = m_tx_data&1;
}
 
return o_rx;
}
 
/sim/verilated/uartsim.h
0,0 → 1,132
////////////////////////////////////////////////////////////////////////////////
//
// Filename: uartsim.h
//
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: To forward a Verilator simulated UART link over a TCP/IP pipe.
//
// This file provides the description of the interface between the UARTSIM
// and the rest of the world. See below for more detailed descriptions.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef UARTSIM_H
#define UARTSIM_H
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
 
#define TXIDLE 0
#define TXDATA 1
#define RXIDLE 0
#define RXDATA 1
 
class UARTSIM {
// The file descriptors:
// m_skt is the socket/port we are listening on
// m_conrd is the file descriptor to read from
// m_conwr is the file descriptor to write to
int m_skt, m_conrd, m_conwr;
//
// The m_setup register is the 29'bit control register used within
// the core.
unsigned m_setup;
// And the pieces of the setup register broken out.
int m_nparity, m_fixdp, m_evenp, m_nbits, m_nstop, m_baud_counts;
 
// UART state
int m_rx_baudcounter, m_rx_state, m_rx_busy,
m_rx_changectr, m_last_tx;
int m_tx_baudcounter, m_tx_state, m_tx_busy;
unsigned m_rx_data, m_tx_data;
 
// setup_listener is an attempt to encapsulate all of the network
// related setup stuff.
void setup_listener(const int port);
 
// nettick() gets called if we are connected to a network, and
int nettick(const int i_tx);
// fdtick() if we are not.
int fdtick(const int i_tx);
 
// We'll use the file descriptor for the listener socket to determine
// whether we are connected to the network or not. If not connected
// to the network, then we assume m_conrd and m_conwr refer to
// your more traditional file descriptors, and use them as such.
int tick(const int i_tx) {
if (m_skt >= 0)
return nettick(i_tx);
else
return fdtick(i_tx);
}
 
public:
//
// The UARTSIM constructor takes one argument: the port on the
// localhost to listen in on. Once started, connections may be made
// to this port to get the output from the port.
UARTSIM(const int port);
 
// kill() closes any active connection and the socket. Once killed,
// no further output will be sent to the port.
void kill(void);
 
// setup() busts out the bits from isetup to the various internal
// parameters. It is ideally only called between bits at appropriate
// transition intervals.
void setup(unsigned isetup);
 
// The operator() function is called on every tick. The input is the
// the output txuart transmit wire from the device. The output is to
// be connected to the the rxuart receive wire into the device. This
// makes hookup and operation very simple.
//
// This is the most appropriate simulation entry function if the
// setup register will never change.
//
int operator()(int i_tx) {
return tick(i_tx); }
 
// If there is a possibility that the core might change the UART setup,
// then it makes sense to include that current setup when calling the
// tick operator.
int operator()(int i_tx, unsigned isetup) {
setup(isetup); return tick(i_tx); }
};
 
#endif
/sim/verilated/zipelf.cpp
0,0 → 1,251
////////////////////////////////////////////////////////////////////////////////
//
// Filename: zipelf.cpp
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
//
// 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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>
#include <assert.h>
#include <gelf.h>
#include <string.h>
 
#include "zipelf.h"
 
bool
iself(const char *fname)
{
FILE *fp;
bool ret = true;
fp = fopen(fname, "rb");
 
if (!fp) return false;
if (0x7f != fgetc(fp)) ret = false;
if ('E' != fgetc(fp)) ret = false;
if ('L' != fgetc(fp)) ret = false;
if ('F' != fgetc(fp)) ret = false;
fclose(fp);
return ret;
}
 
void elfread(const char *fname, unsigned &entry, ELFSECTION **&sections)
{
Elf *e;
int fd, i;
size_t n;
char *id;
Elf_Kind ek;
GElf_Ehdr ehdr;
GElf_Phdr phdr;
const bool dbg = false;
 
if (elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr, "ELF library initialization err, %s\n", elf_errmsg(-1));
perror("O/S Err:");
exit(EXIT_FAILURE);
} if ((fd = open(fname, O_RDONLY, 0)) < 0) {
fprintf(stderr, "Could not open %s\n", fname);
perror("O/S Err:");
exit(EXIT_FAILURE);
} if ((e = elf_begin(fd, ELF_C_READ, NULL))==NULL) {
fprintf(stderr, "Could not run elf_begin, %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
}
 
ek = elf_kind(e);
if (ek == ELF_K_ELF) {
; // This is the kind of file we should expect
} else if (ek == ELF_K_AR) {
fprintf(stderr, "Cannot run an archive!\n");
exit(EXIT_FAILURE);
} else if (ek == ELF_K_NONE) {
;
} else {
fprintf(stderr, "Unexpected ELF file kind!\n");
exit(EXIT_FAILURE);
}
 
if (gelf_getehdr(e, &ehdr) == NULL) {
fprintf(stderr, "getehdr() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
} if ((i=gelf_getclass(e)) == ELFCLASSNONE) {
fprintf(stderr, "getclass() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
} if ((id = elf_getident(e, NULL)) == NULL) {
fprintf(stderr, "getident() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
} if (i != ELFCLASS32) {
fprintf(stderr, "This is a 64-bit ELF file, ZipCPU ELF files are all 32-bit\n");
exit(EXIT_FAILURE);
}
 
if (dbg) {
printf(" %-20s 0x%jx\n", "e_type", (uintmax_t)ehdr.e_type);
printf(" %-20s 0x%jx\n", "e_machine", (uintmax_t)ehdr.e_machine);
printf(" %-20s 0x%jx\n", "e_version", (uintmax_t)ehdr.e_version);
printf(" %-20s 0x%jx\n", "e_entry", (uintmax_t)ehdr.e_entry);
printf(" %-20s 0x%jx\n", "e_phoff", (uintmax_t)ehdr.e_phoff);
printf(" %-20s 0x%jx\n", "e_shoff", (uintmax_t)ehdr.e_shoff);
printf(" %-20s 0x%jx\n", "e_flags", (uintmax_t)ehdr.e_flags);
printf(" %-20s 0x%jx\n", "e_ehsize", (uintmax_t)ehdr.e_ehsize);
printf(" %-20s 0x%jx\n", "e_phentsize", (uintmax_t)ehdr.e_phentsize);
printf(" %-20s 0x%jx\n", "e_shentsize", (uintmax_t)ehdr.e_shentsize);
printf("\n");
}
 
 
// Check whether or not this is an ELF file for the ZipCPU ...
if (ehdr.e_machine != 0x0dad1) {
fprintf(stderr, "This is not a ZipCPU/8 ELF file\n");
exit(EXIT_FAILURE);
}
 
// Get our entry address
entry = ehdr.e_entry;
 
 
// Now, let's go look at the program header
if (elf_getphdrnum(e, &n) != 0) {
fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
}
 
assert(n != 0);
 
unsigned total_octets = 0, current_offset=0, current_section=0;
for(i=0; i<(int)n; i++) {
total_octets += sizeof(ELFSECTION *)+sizeof(ELFSECTION);
 
if (gelf_getphdr(e, i, &phdr) != &phdr) {
fprintf(stderr, "getphdr() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
}
 
if (dbg) {
printf(" %-20s 0x%x\n", "p_type", phdr.p_type);
printf(" %-20s 0x%jx\n", "p_offset", phdr.p_offset);
printf(" %-20s 0x%jx\n", "p_vaddr", phdr.p_vaddr);
printf(" %-20s 0x%jx\n", "p_paddr", phdr.p_paddr);
printf(" %-20s 0x%jx\n", "p_filesz", phdr.p_filesz);
printf(" %-20s 0x%jx\n", "p_memsz", phdr.p_memsz);
printf(" %-20s 0x%x [", "p_flags", phdr.p_flags);
 
if (phdr.p_flags & PF_X) printf(" Execute");
if (phdr.p_flags & PF_R) printf(" Read");
if (phdr.p_flags & PF_W) printf(" Write");
printf("]\n");
printf(" %-20s 0x%jx\n", "p_align", phdr.p_align);
}
 
total_octets += phdr.p_memsz;
}
 
char *d = (char *)malloc(total_octets + sizeof(ELFSECTION)+sizeof(ELFSECTION *));
memset(d, 0, total_octets);
 
ELFSECTION **r = sections = (ELFSECTION **)d;
current_offset = (n+1)*sizeof(ELFSECTION *);
current_section = 0;
 
for(i=0; i<(int)n; i++) {
r[i] = (ELFSECTION *)(&d[current_offset]);
 
if (gelf_getphdr(e, i, &phdr) != &phdr) {
fprintf(stderr, "getphdr() failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
}
 
if (dbg) {
printf(" %-20s 0x%jx\n", "p_offset", phdr.p_offset);
printf(" %-20s 0x%jx\n", "p_vaddr", phdr.p_vaddr);
printf(" %-20s 0x%jx\n", "p_paddr", phdr.p_paddr);
printf(" %-20s 0x%jx\n", "p_filesz", phdr.p_filesz);
printf(" %-20s 0x%jx\n", "p_memsz", phdr.p_memsz);
printf(" %-20s 0x%x [", "p_flags", phdr.p_flags);
 
if (phdr.p_flags & PF_X) printf(" Execute");
if (phdr.p_flags & PF_R) printf(" Read");
if (phdr.p_flags & PF_W) printf(" Write");
printf("]\n");
 
printf(" %-20s 0x%jx\n", "p_align", phdr.p_align);
}
 
current_section++;
 
r[i]->m_start = phdr.p_paddr;
r[i]->m_len = phdr.p_filesz;
 
current_offset += phdr.p_memsz + sizeof(ELFSECTION);
 
// Now, let's read in our section ...
if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
fprintf(stderr, "Could not seek to file position %08lx\n", phdr.p_offset);
perror("O/S Err:");
exit(EXIT_FAILURE);
} if (phdr.p_filesz > phdr.p_memsz)
phdr.p_filesz = 0;
if (read(fd, r[i]->m_data, phdr.p_filesz) != (int)phdr.p_filesz) {
fprintf(stderr, "Didnt read entire section\n");
perror("O/S Err:");
exit(EXIT_FAILURE);
}
 
/*
// Next, we need to byte swap it from big to little endian
for(unsigned j=0; j<r[i]->m_len; j++)
r[i]->m_data[j] = byteswap(r[i]->m_data[j]);
*/
 
if (dbg) for(unsigned j=0; j<r[i]->m_len; j++)
fprintf(stderr, "ADR[%04x] = %02x\n", r[i]->m_start+j,
r[i]->m_data[j] & 0x0ff);
}
 
r[i] = (ELFSECTION *)(&d[current_offset]);
r[current_section]->m_start = 0;
r[current_section]->m_len = 0;
 
elf_end(e);
close(fd);
}
 
/sim/verilated/zipelf.h
0,0 → 1,53
////////////////////////////////////////////////////////////////////////////////
//
// Filename: zipelf.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:
//
//
// 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 ZIPELF_H
#define ZIPELF_H
 
#include <stdint.h>
 
class ELFSECTION {
public:
uint32_t m_start, m_len;
char m_data[4];
};
 
bool iself(const char *fname);
void elfread(const char *fname, uint32_t &entry, ELFSECTION **&sections);
 
#endif
sim/verilated Property changes : Added: svn:ignore ## -0,0 +1,3 ## +.gitignore +obj-pc +*_tb

powered by: WebSVN 2.1.0

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