Add SPI flash support (#546)

* Add SPI flash configs, IOBinders, CI tests, and docs

* Add writable SPI flash support

* bump

* Fix CI

* Fix CI

* Update docs/Generators/TestChipIP.rst

Co-authored-by: Chick Markley <chick@qrhino.com>

* Maybe actually fix CI

* Fix broken merge

* Fix the tutorial patch

* bump tcip to master

* fix GPIO naming bug

Co-authored-by: Chick Markley <chick@qrhino.com>
This commit is contained in:
John Wright
2020-05-14 19:19:50 -07:00
committed by GitHub
parent 1b1f477619
commit 7c7b336c3f
18 changed files with 505 additions and 16 deletions

View File

@@ -12,7 +12,7 @@ parameters:
executors: executors:
main-env: main-env:
docker: docker:
- image: ucbbar/chipyard-image:1.0.0 - image: ucbbar/chipyard-image:1.0.1
environment: environment:
JVM_OPTS: -Xmx3200m # Customize the JVM maximum heap limit JVM_OPTS: -Xmx3200m # Customize the JVM maximum heap limit
@@ -262,6 +262,16 @@ jobs:
steps: steps:
- prepare-rtl: - prepare-rtl:
project-key: "testchipip" project-key: "testchipip"
prepare-chipyard-spiflashwrite:
executor: main-env
steps:
- prepare-rtl:
project-key: "chipyard-spiflashwrite"
prepare-chipyard-spiflashread:
executor: main-env
steps:
- prepare-rtl:
project-key: "chipyard-spiflashread"
chipyard-rocket-run-tests: chipyard-rocket-run-tests:
executor: main-env executor: main-env
steps: steps:
@@ -300,6 +310,16 @@ jobs:
- run-tests: - run-tests:
tools-version: "esp-tools" tools-version: "esp-tools"
project-key: "chipyard-gemmini" project-key: "chipyard-gemmini"
chipyard-spiflashwrite-run-tests:
executor: main-env
steps:
- run-tests:
project-key: "chipyard-spiflashwrite"
chipyard-spiflashread-run-tests:
executor: main-env
steps:
- run-tests:
project-key: "chipyard-spiflashread"
tracegen-run-tests: tracegen-run-tests:
executor: main-env executor: main-env
steps: steps:
@@ -464,6 +484,16 @@ workflows:
- install-riscv-toolchain - install-riscv-toolchain
- install-verilator - install-verilator
- prepare-chipyard-spiflashwrite:
requires:
- install-riscv-toolchain
- install-verilator
- prepare-chipyard-spiflashread:
requires:
- install-riscv-toolchain
- install-verilator
# Run the respective tests # Run the respective tests
# Run midasexamples test # Run midasexamples test
@@ -508,6 +538,14 @@ workflows:
requires: requires:
- prepare-tracegen-boom - prepare-tracegen-boom
- chipyard-spiflashwrite-run-tests:
requires:
- prepare-chipyard-spiflashwrite
- chipyard-spiflashread-run-tests:
requires:
- prepare-chipyard-spiflashread
# Run the firesim tests # Run the firesim tests
- firesim-run-tests: - firesim-run-tests:
requires: requires:

View File

@@ -52,6 +52,8 @@ mapping["chipyard-blkdev"]="SUB_PROJECT=chipyard CONFIG=SimBlockDeviceRocketConf
mapping["chipyard-hwacha"]="SUB_PROJECT=chipyard CONFIG=HwachaRocketConfig" mapping["chipyard-hwacha"]="SUB_PROJECT=chipyard CONFIG=HwachaRocketConfig"
mapping["chipyard-gemmini"]="SUB_PROJECT=chipyard CONFIG=GemminiRocketConfig" mapping["chipyard-gemmini"]="SUB_PROJECT=chipyard CONFIG=GemminiRocketConfig"
mapping["chipyard-ariane"]="SUB_PROJECT=chipyard CONFIG=ArianeConfig" mapping["chipyard-ariane"]="SUB_PROJECT=chipyard CONFIG=ArianeConfig"
mapping["chipyard-spiflashread"]="SUB_PROJECT=chipyard CONFIG=LargeSPIFlashROMRocketConfig"
mapping["chipyard-spiflashwrite"]="SUB_PROJECT=chipyard CONFIG=SmallSPIFlashRocketConfig"
mapping["tracegen"]="SUB_PROJECT=chipyard CONFIG=NonBlockingTraceGenL2Config TOP=TraceGenSystem" mapping["tracegen"]="SUB_PROJECT=chipyard CONFIG=NonBlockingTraceGenL2Config TOP=TraceGenSystem"
mapping["tracegen-boom"]="SUB_PROJECT=chipyard CONFIG=BoomTraceGenConfig TOP=TraceGenSystem" mapping["tracegen-boom"]="SUB_PROJECT=chipyard CONFIG=BoomTraceGenConfig TOP=TraceGenSystem"
mapping["firesim"]="SCALA_TEST=firesim.firesim.RocketNICF1Tests" mapping["firesim"]="SCALA_TEST=firesim.firesim.RocketNICF1Tests"

View File

@@ -13,6 +13,8 @@ RUN apt-get update \
git \ git \
gnupg \ gnupg \
gzip \ gzip \
libfl2 \
libfl-dev \
locales \ locales \
mercurial \ mercurial \
netcat \ netcat \
@@ -24,7 +26,12 @@ RUN apt-get update \
unzip \ unzip \
wget \ wget \
xvfb \ xvfb \
zip xxd \
zip \
ccache \
libgoogle-perftools-dev \
numactl \
zlib1g
# Set timezone to UTC by default # Set timezone to UTC by default
RUN ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime RUN ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime
@@ -127,34 +134,46 @@ RUN apt-get install -y --no-install-recommends openjfx
RUN apt-get install -y build-essential RUN apt-get install -y build-essential
# Add RISCV toolchain necessary dependencies # Add RISCV toolchain necessary dependencies
RUN apt-get update
RUN apt-get install -y \ RUN apt-get install -y \
autoconf \ autoconf \
automake \ automake \
autotools-dev \ autotools-dev \
babeltrace \ babeltrace \
bc \ bc \
bison \
curl \ curl \
device-tree-compiler \ device-tree-compiler \
expat \ expat \
flex \ flex \
gawk \ gawk \
gperf \ gperf \
g++ \
libexpat-dev \ libexpat-dev \
libgmp-dev \ libgmp-dev \
libmpc-dev \ libmpc-dev \
libmpfr-dev \ libmpfr-dev \
libtool \ libtool \
libusb-1.0-0-dev \ libusb-1.0-0-dev \
make \
patchutils \ patchutils \
pkg-config \ pkg-config \
python \ python \
python-pexpect \ python-pexpect-doc \
python3 \ python3 \
texinfo \ texinfo \
zlib1g-dev \ zlib1g-dev \
rsync rsync
# Use specific bison version to bypass Verilator 4.034 issues
# TODO: When Verilator is bumped, use apt to get newest bison
RUN wget https://ftp.gnu.org/gnu/bison/bison-3.5.4.tar.gz \
&& tar -xvf bison-3.5.4.tar.gz \
&& cd bison-3.5.4 \
&& ./configure && make && make install
# Check bison version is 3.5.4
RUN bison --version
# Add minimal QEMU dependencies # Add minimal QEMU dependencies
RUN apt-get install -y \ RUN apt-get install -y \
libfdt-dev \ libfdt-dev \
@@ -164,6 +183,7 @@ RUN apt-get install -y \
# Install verilator # Install verilator
RUN git clone http://git.veripool.org/git/verilator \ RUN git clone http://git.veripool.org/git/verilator \
&& cd verilator \ && cd verilator \
&& git pull \
&& git checkout v4.034 \ && git checkout v4.034 \
&& autoconf && ./configure && make && make install && autoconf && ./configure && make && make install

View File

@@ -62,6 +62,15 @@ case $1 in
(cd $LOCAL_CHIPYARD_DIR/generators/sha3/software && ./build.sh) (cd $LOCAL_CHIPYARD_DIR/generators/sha3/software && ./build.sh)
$LOCAL_SIM_DIR/simulator-chipyard-Sha3RocketConfig $LOCAL_CHIPYARD_DIR/generators/sha3/software/benchmarks/bare/sha3-rocc.riscv $LOCAL_SIM_DIR/simulator-chipyard-Sha3RocketConfig $LOCAL_CHIPYARD_DIR/generators/sha3/software/benchmarks/bare/sha3-rocc.riscv
;; ;;
chipyard-spiflashread)
make -C $LOCAL_CHIPYARD_DIR/tests
make -C $LOCAL_SIM_DIR ${mapping[$1]} BINARY=$LOCAL_CHIPYARD_DIR/tests/spiflashread.riscv SIM_FLAGS="+spiflash0=${LOCAL_CHIPYARD_DIR}/tests/spiflash.img" run-binary
;;
chipyard-spiflashwrite)
make -C $LOCAL_CHIPYARD_DIR/tests
make -C $LOCAL_SIM_DIR ${mapping[$1]} BINARY=$LOCAL_CHIPYARD_DIR/tests/spiflashwrite.riscv SIM_FLAGS="+spiflash0=${LOCAL_CHIPYARD_DIR}/tests/spiflash.img" run-binary
[[ "`xxd $LOCAL_CHIPYARD_DIR/tests/spiflash.img | grep 1337\ 00ff\ aa55\ face | wc -l`" == "6" ]] || false
;;
tracegen) tracegen)
run_tracegen ${mapping[$1]} run_tracegen ${mapping[$1]}
;; ;;

View File

@@ -84,3 +84,11 @@ output a UART log to a particular file using ``+uartlog=<NAME_OF_FILE>`` during
By default, this UART Adapter is added to all systems within Chipyard by adding the By default, this UART Adapter is added to all systems within Chipyard by adding the
``WithUART`` and ``WithUARTAdapter`` configs. ``WithUART`` and ``WithUARTAdapter`` configs.
SPI Flash Model
---------------
The SPI flash model is a device that models a simple SPI flash device. It currently
only supports single read, quad read, single write, and quad write instructions. The
memory is backed by a file which is provided using ``+spiflash#=<NAME_OF_FILE>``,
where ``#`` is the SPI flash ID (usually ``0``).

View File

@@ -20,6 +20,7 @@ import hwacha.{Hwacha}
import sifive.blocks.devices.gpio._ import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._ import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._
import chipyard.{BuildTop, BuildSystem} import chipyard.{BuildTop, BuildSystem}
@@ -52,6 +53,12 @@ class WithUART extends Config((site, here, up) => {
UARTParams(address = 0x54000000L, nTxEntries = 256, nRxEntries = 256)) UARTParams(address = 0x54000000L, nTxEntries = 256, nRxEntries = 256))
}) })
class WithSPIFlash(size: BigInt = 0x10000000) extends Config((site, here, up) => {
// Note: the default size matches freedom with the addresses below
case PeripherySPIFlashKey => Seq(
SPIFlashParams(rAddress = 0x10040000, fAddress = 0x20000000, fSize = size))
})
class WithL2TLBs(entries: Int) extends Config((site, here, up) => { class WithL2TLBs(entries: Int) extends Config((site, here, up) => {
case RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy( case RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy(
core = tile.core.copy(nL2TLBEntries = entries) core = tile.core.copy(nL2TLBEntries = entries)

View File

@@ -19,6 +19,7 @@ class DigitalTop(implicit p: Parameters) extends System
with testchipip.CanHavePeripherySerial // Enables optionally adding the TSI serial-adapter and port with testchipip.CanHavePeripherySerial // Enables optionally adding the TSI serial-adapter and port
with sifive.blocks.devices.uart.HasPeripheryUART // Enables optionally adding the sifive UART with sifive.blocks.devices.uart.HasPeripheryUART // Enables optionally adding the sifive UART
with sifive.blocks.devices.gpio.HasPeripheryGPIO // Enables optionally adding the sifive GPIOs with sifive.blocks.devices.gpio.HasPeripheryGPIO // Enables optionally adding the sifive GPIOs
with sifive.blocks.devices.spi.HasPeripherySPIFlash // Enables optionally adding the sifive SPI flash controller
with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim
with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget
with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget
@@ -32,6 +33,7 @@ class DigitalTopModule[+L <: DigitalTop](l: L) extends SystemModule(l)
with testchipip.CanHavePeripherySerialModuleImp with testchipip.CanHavePeripherySerialModuleImp
with sifive.blocks.devices.uart.HasPeripheryUARTModuleImp with sifive.blocks.devices.uart.HasPeripheryUARTModuleImp
with sifive.blocks.devices.gpio.HasPeripheryGPIOModuleImp with sifive.blocks.devices.gpio.HasPeripheryGPIOModuleImp
with sifive.blocks.devices.spi.HasPeripherySPIFlashModuleImp
with icenet.CanHavePeripheryIceNICModuleImp with icenet.CanHavePeripheryIceNICModuleImp
with chipyard.example.CanHavePeripheryGCDModuleImp with chipyard.example.CanHavePeripheryGCDModuleImp
with freechips.rocketchip.util.DontTouch with freechips.rocketchip.util.DontTouch

View File

@@ -13,6 +13,7 @@ import freechips.rocketchip.util._
import sifive.blocks.devices.gpio._ import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._ import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._
import barstools.iocell.chisel._ import barstools.iocell.chisel._
@@ -88,10 +89,8 @@ object AddIOCells {
def gpio(gpios: Seq[GPIOPortIO], genFn: () => DigitalGPIOCell = IOCell.genericGPIO): (Seq[Seq[Analog]], Seq[Seq[IOCell]]) = { def gpio(gpios: Seq[GPIOPortIO], genFn: () => DigitalGPIOCell = IOCell.genericGPIO): (Seq[Seq[Analog]], Seq[Seq[IOCell]]) = {
gpios.zipWithIndex.map({ case (gpio, i) => gpios.zipWithIndex.map({ case (gpio, i) =>
gpio.pins.zipWithIndex.map({ case (pin, j) => gpio.pins.zipWithIndex.map({ case (pin, j) =>
val g = IO(Analog(1.W)) val g = IO(Analog(1.W)).suggestName(s"gpio_${i}_${j}")
g.suggestName("gpio_${i}_${j}") val iocell = genFn().suggestName(s"iocell_gpio_${i}_${j}")
val iocell = genFn()
iocell.suggestName(s"iocell_gpio_${i}_${j}")
iocell.io.o := pin.o.oval iocell.io.o := pin.o.oval
iocell.io.oe := pin.o.oe iocell.io.oe := pin.o.oe
iocell.io.ie := pin.o.ie iocell.io.ie := pin.o.ie
@@ -115,6 +114,37 @@ object AddIOCells {
}).unzip }).unzip
} }
/**
* Add IO cells to a SiFive SPI devices and name the IO ports.
* @param spiPins A Seq of SPI port bundles
* @param basename The base name for this port (defaults to "spi")
* @param genFn A callable function to generate a DigitalGPIOCell module to use
* @return Returns a tuple of (A Seq of top-level SPIChipIO IOs; a 2D Seq of IOCell module references)
*/
def spi(spiPins: Seq[SPIPortIO], basename: String = "spi", genFn: () => DigitalGPIOCell = IOCell.genericGPIO): (Seq[SPIChipIO], Seq[Seq[IOCell]]) = {
spiPins.zipWithIndex.map({ case (s, i) =>
val port = IO(new SPIChipIO(s.c.csWidth)).suggestName(s"${basename}_${i}")
val iocellBase = s"iocell_${basename}_${i}"
// SCK and CS are unidirectional outputs
val sckIOs = IOCell.generateFromSignal(s.sck, port.sck, Some(s"${iocellBase}_sck"))
val csIOs = IOCell.generateFromSignal(s.cs, port.cs, Some(s"${iocellBase}_cs"))
// DQ are bidirectional, so then need special treatment
val dqIOs = s.dq.zip(port.dq).zipWithIndex.map { case ((pin, ana), j) =>
val iocell = genFn().suggestName(s"${iocellBase}_dq_${j}")
iocell.io.o := pin.o
iocell.io.oe := pin.oe
iocell.io.ie := true.B
pin.i := iocell.io.i
iocell.io.pad <> ana
iocell
}
(port, dqIOs ++ csIOs ++ sckIOs)
}).unzip
}
/** /**
* Add IO cells to a debug module and name the IO ports. * Add IO cells to a debug module and name the IO ports.
* @param psd A PSDIO bundle * @param psd A PSDIO bundle
@@ -172,6 +202,14 @@ class WithUARTAdapter extends OverrideIOBinder({
} }
}) })
class WithSimSPIFlashModel(rdOnly: Boolean = true) extends OverrideIOBinder({
(system: HasPeripherySPIFlashModuleImp) => {
val (ports, ioCells2d) = AddIOCells.spi(system.qspi, "qspi")
val harnessFn = (th: chipyard.TestHarness) => { SimSPIFlashModel.connect(ports, th.reset, rdOnly)(system.p); Nil }
Seq((ports, ioCells2d.flatten, Some(harnessFn)))
}
})
class WithSimBlockDevice extends OverrideIOBinder({ class WithSimBlockDevice extends OverrideIOBinder({
(system: CanHavePeripheryBlockDeviceModuleImp) => system.connectSimBlockDevice(system.clock, system.reset.asBool); Nil (system: CanHavePeripheryBlockDeviceModuleImp) => system.connectSimBlockDevice(system.clock, system.reset.asBool); Nil
}) })

View File

@@ -165,6 +165,46 @@ class GCDAXI4BlackBoxRocketConfig extends Config(
new freechips.rocketchip.system.BaseConfig) new freechips.rocketchip.system.BaseConfig)
// DOC include end: GCDAXI4BlackBoxRocketConfig // DOC include end: GCDAXI4BlackBoxRocketConfig
class LargeSPIFlashROMRocketConfig extends Config(
new chipyard.iobinders.WithUARTAdapter ++
new chipyard.iobinders.WithTieOffInterrupts ++
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTiedOffDebug ++
new chipyard.iobinders.WithSimSerial ++
new chipyard.iobinders.WithSimSPIFlashModel(true) ++ // add the SPI flash model in the harness (read-only)
new testchipip.WithTSI ++
new chipyard.config.WithBootROM ++
new chipyard.config.WithUART ++
new chipyard.config.WithSPIFlash ++ // add the SPI flash controller
new chipyard.config.WithL2TLBs(1024) ++
new freechips.rocketchip.subsystem.WithNoMMIOPort ++
new freechips.rocketchip.subsystem.WithNoSlavePort ++
new freechips.rocketchip.subsystem.WithInclusiveCache ++
new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
new freechips.rocketchip.subsystem.WithCoherentBusTopology ++
new freechips.rocketchip.system.BaseConfig)
class SmallSPIFlashRocketConfig extends Config(
new chipyard.iobinders.WithUARTAdapter ++
new chipyard.iobinders.WithTieOffInterrupts ++
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTiedOffDebug ++
new chipyard.iobinders.WithSimSerial ++
new chipyard.iobinders.WithSimSPIFlashModel(false) ++ // add the SPI flash model in the harness (writeable)
new testchipip.WithTSI ++
new chipyard.config.WithBootROM ++
new chipyard.config.WithUART ++
new chipyard.config.WithSPIFlash(0x100000) ++ // add the SPI flash controller (1 MiB)
new chipyard.config.WithL2TLBs(1024) ++
new freechips.rocketchip.subsystem.WithNoMMIOPort ++
new freechips.rocketchip.subsystem.WithNoSlavePort ++
new freechips.rocketchip.subsystem.WithInclusiveCache ++
new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
new freechips.rocketchip.subsystem.WithCoherentBusTopology ++
new freechips.rocketchip.system.BaseConfig)
class SimBlockDeviceRocketConfig extends Config( class SimBlockDeviceRocketConfig extends Config(
new chipyard.iobinders.WithUARTAdapter ++ new chipyard.iobinders.WithUARTAdapter ++
new chipyard.iobinders.WithTieOffInterrupts ++ new chipyard.iobinders.WithTieOffInterrupts ++

View File

@@ -38,8 +38,8 @@ extern remote_bitbang_t * jtag;
extern int dramsim; extern int dramsim;
static uint64_t trace_count = 0; static uint64_t trace_count = 0;
bool verbose; bool verbose = false;
bool done_reset; bool done_reset = false;
void handle_sigterm(int sig) void handle_sigterm(int sig)
{ {
@@ -282,6 +282,10 @@ done_processing:
signal(SIGTERM, handle_sigterm); signal(SIGTERM, handle_sigterm);
bool dump; bool dump;
// start reset off low so a rising edge triggers async reset
tile->reset = 0;
tile->clock = 0;
tile->eval();
// reset for several cycles to handle pipelined reset // reset for several cycles to handle pipelined reset
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
tile->reset = 1; tile->reset = 1;

View File

@@ -1,8 +1,8 @@
diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala
index bc1dab6..1d84129 100644 index 49d2238..afaa36d 100644
--- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala
+++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala
@@ -293,7 +293,7 @@ class Sha3RocketConfig extends Config( @@ -333,7 +333,7 @@ class Sha3RocketConfig extends Config(
new chipyard.config.WithBootROM ++ new chipyard.config.WithBootROM ++
new chipyard.config.WithUART ++ new chipyard.config.WithUART ++
new chipyard.config.WithL2TLBs(1024) ++ new chipyard.config.WithL2TLBs(1024) ++

1
tests/.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.o *.o
*.riscv *.riscv
*.dump *.dump
*.img
libgloss/ libgloss/

View File

@@ -5,12 +5,15 @@ LDFLAGS= -static
include libgloss.mk include libgloss.mk
PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd spiflashread spiflashwrite
spiflash.img: spiflash.py
python3 $<
.DEFAULT_GOAL := default .DEFAULT_GOAL := default
.PHONY: default .PHONY: default
default: $(addsuffix .riscv,$(PROGRAMS)) default: $(addsuffix .riscv,$(PROGRAMS)) spiflash.img
.PHONY: dumps .PHONY: dumps
dumps: $(addsuffix .dump,$(PROGRAMS)) dumps: $(addsuffix .dump,$(PROGRAMS))
@@ -18,7 +21,7 @@ dumps: $(addsuffix .dump,$(PROGRAMS))
%.o: %.S %.o: %.S
$(GCC) $(CFLAGS) -D__ASSEMBLY__=1 -c $< -o $@ $(GCC) $(CFLAGS) -D__ASSEMBLY__=1 -c $< -o $@
%.o: %.c mmio.h %.o: %.c mmio.h spiflash.h
$(GCC) $(CFLAGS) -c $< -o $@ $(GCC) $(CFLAGS) -c $< -o $@
%.riscv: %.o $(libgloss) %.riscv: %.o $(libgloss)

174
tests/spiflash.h Normal file
View File

@@ -0,0 +1,174 @@
#ifndef __SPIFLASH_H__
#define __SPIFLASH_H__
// These are configuration-dependent, but for the unit test we'll use the example config
#define SPIFLASH_BASE_MEM 0x20000000
#define SPIFLASH_BASE_MEM_SIZE 0x10000000
#define SPIFLASH_BASE_CTRL 0x10040000
// Only defining the registers we use; there are more
// Software control
#define SPIFLASH_OFFS_CSMODE 0x18
#define SPIFLASH_OFFS_FMT 0x40
#define SPIFLASH_OFFS_TXDATA 0x48
#define SPIFLASH_OFFS_RXDATA 0x4c
// Hardware state machine control
#define SPIFLASH_OFFS_FLASH_EN 0x60
#define SPIFLASH_OFFS_FFMT 0x64
// chip select modes
#define CSMODE_AUTO 0
#define CSMODE_HOLD 2
#define CSMODE_OFF 3
// SPI flash protocol settings
#define SPIFLASH_PROTO_SINGLE 0
#define SPIFLASH_PROTO_DUAL 1
#define SPIFLASH_PROTO_QUAD 2
// SPI flash IO settings
#define SPIFLASH_IODIR_RX 0
#define SPIFLASH_IODIR_TX 1
// SPI flash endianness settings
#define SPIFLASH_ENDIAN_MSB 0
#define SPIFLASH_ENDIAN_LSB 1
static uint8_t test_data[] = {0x13,0x37,0x00,0xff,0xaa,0x55,0xfa,0xce,0x0f,0xf0,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
static uint8_t test_len = 16;
typedef union
{
struct {
unsigned int proto : 2;
unsigned int endian : 1;
unsigned int iodir : 1;
unsigned int : 12;
unsigned int len : 4;
unsigned int : 12;
} fields;
uint32_t bits;
} spi_fmt;
typedef union
{
struct {
unsigned int cmd_en : 1;
unsigned int addr_len : 3;
unsigned int pad_cnt : 4;
unsigned int cmd_proto : 2;
unsigned int addr_proto : 2;
unsigned int data_proto : 2;
unsigned int : 2;
unsigned int cmd_code : 8;
unsigned int pad_code : 8;
} fields;
uint32_t bits;
} spiflash_ffmt;
// send something to the SPI TX
void spi_data_write(uint8_t data)
{
while (reg_read32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_TXDATA) >= 0x80000000);
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_TXDATA, (uint32_t)data);
}
// configure the hardware flash controller
void configure_spiflash(spiflash_ffmt data)
{
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FLASH_EN, 0);
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FFMT, data.bits);
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FLASH_EN, 1);
}
// write some data to the flash using software (there is no hardware write controller)
void write_spiflash(uint8_t *data, uint32_t len, uint32_t addr, uint8_t cmd, uint8_t abytes, uint8_t aproto, uint8_t dproto)
{
spi_fmt fmt;
fmt.fields.proto = SPIFLASH_PROTO_SINGLE;
fmt.fields.endian = SPIFLASH_ENDIAN_MSB;
fmt.fields.iodir = SPIFLASH_IODIR_TX;
fmt.fields.len = 8;
uint32_t i;
// Need to be out of flash mode
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FLASH_EN, 0);
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FMT, fmt.bits);
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_CSMODE, CSMODE_HOLD);
spi_data_write(cmd);
// need to wait a bit to flush the tx queue before changing fmt
for(i = 0; i < 0x100; i++) asm volatile ("nop");
fmt.fields.proto = aproto;
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FMT, fmt.bits);
for (i = abytes; i > 0; i--)
{
spi_data_write((uint8_t)(addr >> (i*8-8)));
}
// need to wait a bit to flush the tx queue before changing fmt
for(i = 0; i < 0x100; i++) asm volatile ("nop");
fmt.fields.proto = dproto;
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FMT, fmt.bits);
for (i = 0; i < len; i++)
{
spi_data_write(data[i]);
}
// need to wait a bit to flush the tx queue before deasserting CS
for(i = 0; i < 0x100; i++) asm volatile ("nop");
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_CSMODE, CSMODE_OFF);
// go back into flash read mode
reg_write32(SPIFLASH_BASE_CTRL + SPIFLASH_OFFS_FLASH_EN, 1);
}
// test that a large chunk of memory contains (0xdeadbeef - address) or 0
int test_spiflash(uint32_t start, uint32_t size, uint8_t zero)
{
uint32_t i;
for (i = start; i < (start + size); i += 4)
{
uint32_t data = reg_read32(SPIFLASH_BASE_MEM + i);
uint32_t check = 0;
if (!zero) check = 0xdeadbeef - i;
if(data != check)
{
printf("Error reading address 0x%08x from SPI flash. Got 0x%08x, expected 0x%08x.\n", i, data, check);
return 1;
}
}
return 0;
}
// this is a variant of test_spiflash that only tests a small array of values
int check_write(uint8_t *check, uint32_t len, uint32_t addr)
{
uint32_t i;
for (i = 0; i < len; i += 4)
{
uint32_t data = reg_read32(SPIFLASH_BASE_MEM + addr + i);
uint32_t check32 = ((uint32_t *)check)[i/4];
if(check32 != data)
{
printf("Error reading address 0x%08x from SPI flash. Got 0x%02x, expected 0x%02x.\n", i + addr, data, check32);
return 1;
}
}
return 0;
}
#endif /* __SPIFLASH_H__ */

11
tests/spiflash.py Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python3
# Generates a binary file that the SPI test uses
outfile = "spiflash.img"
with open(outfile, 'wb') as f:
for i in range(0,0x100000,4):
check = 0xdeadbeef - i
f.write(check.to_bytes(4,'little'))

77
tests/spiflashread.c Normal file
View File

@@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdio.h>
#include "mmio.h"
#include "spiflash.h"
int main(void)
{
spiflash_ffmt ffmt;
ffmt.fields.cmd_en = 1;
ffmt.fields.addr_len = 4; // Valid options are 3 or 4 for our model
ffmt.fields.pad_cnt = 0; // Our SPI flash model assumes 8 dummy cycles for fast reads, 0 for slow
ffmt.fields.cmd_proto = SPIFLASH_PROTO_SINGLE; // Our SPI flash model only supports single-bit commands
ffmt.fields.addr_proto = SPIFLASH_PROTO_SINGLE; // We support both single and quad
ffmt.fields.data_proto = SPIFLASH_PROTO_SINGLE; // We support both single and quad
ffmt.fields.cmd_code = 0x13; // Slow read 4 byte
ffmt.fields.pad_code = 0x00; // Not used by our model
printf("Testing SPI flash command 0x13...\n");
configure_spiflash(ffmt);
if (test_spiflash(0x0, 0x100, 0)) return 1;
printf("Testing SPI flash command 0x03...\n");
ffmt.fields.cmd_code = 0x03; // Slow read 3 byte address
ffmt.fields.addr_len = 3; // 3 byte address
configure_spiflash(ffmt);
if (test_spiflash(0x0, 0x100, 0)) return 1;
printf("Testing SPI flash command 0x0B...\n");
ffmt.fields.cmd_code = 0x0B; // Fast read 3 byte address
ffmt.fields.pad_cnt = 8; // Needs to be 8 for fast read
configure_spiflash(ffmt);
if (test_spiflash(0x1000, 0x100, 0)) return 1;
printf("Testing SPI flash command 0x0C...\n");
ffmt.fields.cmd_code = 0x0C; // Fast read 4 byte address
ffmt.fields.addr_len = 4; // 4 byte address
configure_spiflash(ffmt);
if (test_spiflash(0x2340, 0x100, 0)) return 1;
printf("Testing SPI flash command 0x6C...\n");
ffmt.fields.cmd_code = 0x6C; // Fast read 4 byte address, quad data
ffmt.fields.data_proto = SPIFLASH_PROTO_QUAD; // Quad data
configure_spiflash(ffmt);
if (test_spiflash(0x410c, 0x100, 0)) return 1;
printf("Testing SPI flash command 0x6B...\n");
ffmt.fields.cmd_code = 0x6B; // Fast read 3 byte address, quad data
ffmt.fields.addr_len = 3;
configure_spiflash(ffmt);
if (test_spiflash(0x5ff8, 0x100, 0)) return 1;
printf("Testing SPI flash command 0xEB...\n");
ffmt.fields.cmd_code = 0xEB; // Fast read 3 byte address, quad data, quad addr
ffmt.fields.addr_proto = SPIFLASH_PROTO_QUAD;
configure_spiflash(ffmt);
if (test_spiflash(0x7c04, 0x100, 0)) return 1;
printf("Testing SPI flash command 0xEC...\n");
ffmt.fields.cmd_code = 0xEC; // Fast read 4 byte address, quad data, quad addr
ffmt.fields.addr_len = 4;
configure_spiflash(ffmt);
if (test_spiflash(0x9000, 0x100, 0)) return 1;
printf("Testing SPI flash extended range...\n");
// The provided memory image is only 1MiB, but the model has 16MiB of addressable space
// This should return 0
if (test_spiflash(0x100000, 0x100, 1)) return 1;
// This write should do nothing, so we can just re-test the first test
printf("Testing that the SPI is not writable...\n");
write_spiflash(test_data, test_len, 0x0, 0x3E, 4, SPIFLASH_PROTO_QUAD, SPIFLASH_PROTO_QUAD);
if (test_spiflash(0x0, 0x100, 0)) return 1;
return 0;
}

55
tests/spiflashwrite.c Normal file
View File

@@ -0,0 +1,55 @@
#include <stdlib.h>
#include <stdio.h>
#include "mmio.h"
#include "spiflash.h"
int main(void)
{
spiflash_ffmt ffmt;
ffmt.fields.cmd_en = 1;
ffmt.fields.addr_len = 4; // Valid options are 3 or 4 for our model
ffmt.fields.pad_cnt = 0; // Our SPI flash model assumes 8 dummy cycles for fast reads, 0 for slow
ffmt.fields.cmd_proto = SPIFLASH_PROTO_SINGLE; // Our SPI flash model only supports single-bit commands
ffmt.fields.addr_proto = SPIFLASH_PROTO_SINGLE; // We support both single and quad
ffmt.fields.data_proto = SPIFLASH_PROTO_SINGLE; // We support both single and quad
ffmt.fields.cmd_code = 0x13; // Slow read 4 byte
ffmt.fields.pad_code = 0x00; // Not used by our model
// Test that we can read
printf("Testing SPI flash command 0x13...\n");
configure_spiflash(ffmt);
if (test_spiflash(0x0, 0x100, 0)) return 1;
// 0x02: 3 byte addr, single/single
printf("Testing SPI flash command 0x02...\n");
write_spiflash(test_data, test_len, 0x200, 0x02, 3, SPIFLASH_PROTO_SINGLE, SPIFLASH_PROTO_SINGLE);
if (check_write(test_data, test_len, 0x200)) return 1;
// 0x32: 3 byte addr, single/quad
printf("Testing SPI flash command 0x32...\n");
write_spiflash(test_data, test_len, 0x300, 0x32, 3, SPIFLASH_PROTO_SINGLE, SPIFLASH_PROTO_QUAD);
if (check_write(test_data, test_len, 0x300)) return 1;
// 0x38: 3 byte addr, quad/quad
printf("Testing SPI flash command 0x38...\n");
write_spiflash(test_data, test_len, 0x400, 0x38, 3, SPIFLASH_PROTO_QUAD, SPIFLASH_PROTO_QUAD);
if (check_write(test_data, test_len, 0x400)) return 1;
// 0x12: 4 byte addr, single/single
printf("Testing SPI flash command 0x12...\n");
write_spiflash(test_data, test_len, 0x500, 0x12, 4, SPIFLASH_PROTO_SINGLE, SPIFLASH_PROTO_SINGLE);
if (check_write(test_data, test_len, 0x500)) return 1;
// 0x34: 4 byte addr, single/quad
printf("Testing SPI flash command 0x34...\n");
write_spiflash(test_data, test_len, 0x600, 0x34, 4, SPIFLASH_PROTO_SINGLE, SPIFLASH_PROTO_QUAD);
if (check_write(test_data, test_len, 0x600)) return 1;
// 0x3E: 4 byte addr, quad/quad
printf("Testing SPI flash command 0x3E...\n");
write_spiflash(test_data, test_len, 0x700, 0x3E, 4, SPIFLASH_PROTO_QUAD, SPIFLASH_PROTO_QUAD);
if (check_write(test_data, test_len, 0x700)) return 1;
return 0;
}