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 **§ions) |
{ |
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 **§ions); |
|
#endif |
sim/verilated
Property changes :
Added: svn:ignore
## -0,0 +1,3 ##
+.gitignore
+obj-pc
+*_tb