diff --git a/.circleci/config.yml b/.circleci/config.yml index 4d849e77..8e88f7eb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ parameters: executors: main-env: docker: - - image: ucbbar/chipyard-image:1.0.0 + - image: ucbbar/chipyard-image:1.0.1 environment: JVM_OPTS: -Xmx3200m # Customize the JVM maximum heap limit @@ -262,6 +262,16 @@ jobs: steps: - prepare-rtl: 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: executor: main-env steps: @@ -300,6 +310,16 @@ jobs: - run-tests: tools-version: "esp-tools" 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: executor: main-env steps: @@ -464,6 +484,16 @@ workflows: - install-riscv-toolchain - 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 midasexamples test @@ -508,6 +538,14 @@ workflows: requires: - prepare-tracegen-boom + - chipyard-spiflashwrite-run-tests: + requires: + - prepare-chipyard-spiflashwrite + + - chipyard-spiflashread-run-tests: + requires: + - prepare-chipyard-spiflashread + # Run the firesim tests - firesim-run-tests: requires: diff --git a/.circleci/defaults.sh b/.circleci/defaults.sh index 3420a1a2..e944e7fd 100755 --- a/.circleci/defaults.sh +++ b/.circleci/defaults.sh @@ -52,6 +52,8 @@ mapping["chipyard-blkdev"]="SUB_PROJECT=chipyard CONFIG=SimBlockDeviceRocketConf mapping["chipyard-hwacha"]="SUB_PROJECT=chipyard CONFIG=HwachaRocketConfig" mapping["chipyard-gemmini"]="SUB_PROJECT=chipyard CONFIG=GemminiRocketConfig" 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-boom"]="SUB_PROJECT=chipyard CONFIG=BoomTraceGenConfig TOP=TraceGenSystem" mapping["firesim"]="SCALA_TEST=firesim.firesim.RocketNICF1Tests" diff --git a/.circleci/images/Dockerfile b/.circleci/images/Dockerfile index 3efb4cc9..7d031850 100644 --- a/.circleci/images/Dockerfile +++ b/.circleci/images/Dockerfile @@ -13,6 +13,8 @@ RUN apt-get update \ git \ gnupg \ gzip \ + libfl2 \ + libfl-dev \ locales \ mercurial \ netcat \ @@ -24,7 +26,12 @@ RUN apt-get update \ unzip \ wget \ xvfb \ - zip + xxd \ + zip \ + ccache \ + libgoogle-perftools-dev \ + numactl \ + zlib1g # Set timezone to UTC by default 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 # Add RISCV toolchain necessary dependencies +RUN apt-get update RUN apt-get install -y \ autoconf \ automake \ autotools-dev \ babeltrace \ bc \ - bison \ curl \ device-tree-compiler \ expat \ flex \ gawk \ gperf \ + g++ \ libexpat-dev \ libgmp-dev \ libmpc-dev \ libmpfr-dev \ libtool \ libusb-1.0-0-dev \ + make \ patchutils \ pkg-config \ python \ - python-pexpect \ + python-pexpect-doc \ python3 \ texinfo \ zlib1g-dev \ 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 RUN apt-get install -y \ libfdt-dev \ @@ -164,6 +183,7 @@ RUN apt-get install -y \ # Install verilator RUN git clone http://git.veripool.org/git/verilator \ && cd verilator \ + && git pull \ && git checkout v4.034 \ && autoconf && ./configure && make && make install diff --git a/.circleci/run-tests.sh b/.circleci/run-tests.sh index 5f305617..c7794173 100755 --- a/.circleci/run-tests.sh +++ b/.circleci/run-tests.sh @@ -62,6 +62,15 @@ case $1 in (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 ;; + 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) run_tracegen ${mapping[$1]} ;; diff --git a/docs/Generators/TestChipIP.rst b/docs/Generators/TestChipIP.rst index 31f9ce53..363e3245 100644 --- a/docs/Generators/TestChipIP.rst +++ b/docs/Generators/TestChipIP.rst @@ -84,3 +84,11 @@ output a UART log to a particular file using ``+uartlog=`` during By default, this UART Adapter is added to all systems within Chipyard by adding the ``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#=``, +where ``#`` is the SPI flash ID (usually ``0``). diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 2db12ff7..c2fec56f 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -20,6 +20,7 @@ import hwacha.{Hwacha} import sifive.blocks.devices.gpio._ import sifive.blocks.devices.uart._ +import sifive.blocks.devices.spi._ import chipyard.{BuildTop, BuildSystem} @@ -52,6 +53,12 @@ class WithUART extends Config((site, here, up) => { 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) => { case RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy( core = tile.core.copy(nL2TLBEntries = entries) diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index c7b3d497..e8fb4f4c 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -19,6 +19,7 @@ class DigitalTop(implicit p: Parameters) extends System 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.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 chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero 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 sifive.blocks.devices.uart.HasPeripheryUARTModuleImp with sifive.blocks.devices.gpio.HasPeripheryGPIOModuleImp + with sifive.blocks.devices.spi.HasPeripherySPIFlashModuleImp with icenet.CanHavePeripheryIceNICModuleImp with chipyard.example.CanHavePeripheryGCDModuleImp with freechips.rocketchip.util.DontTouch diff --git a/generators/chipyard/src/main/scala/IOBinders.scala b/generators/chipyard/src/main/scala/IOBinders.scala index ea0f610b..97cc0ba9 100644 --- a/generators/chipyard/src/main/scala/IOBinders.scala +++ b/generators/chipyard/src/main/scala/IOBinders.scala @@ -13,6 +13,7 @@ import freechips.rocketchip.util._ import sifive.blocks.devices.gpio._ import sifive.blocks.devices.uart._ +import sifive.blocks.devices.spi._ 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]]) = { gpios.zipWithIndex.map({ case (gpio, i) => gpio.pins.zipWithIndex.map({ case (pin, j) => - val g = IO(Analog(1.W)) - g.suggestName("gpio_${i}_${j}") - val iocell = genFn() - iocell.suggestName(s"iocell_gpio_${i}_${j}") + val g = IO(Analog(1.W)).suggestName(s"gpio_${i}_${j}") + val iocell = genFn().suggestName(s"iocell_gpio_${i}_${j}") iocell.io.o := pin.o.oval iocell.io.oe := pin.o.oe iocell.io.ie := pin.o.ie @@ -115,6 +114,37 @@ object AddIOCells { }).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. * @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({ (system: CanHavePeripheryBlockDeviceModuleImp) => system.connectSimBlockDevice(system.clock, system.reset.asBool); Nil }) diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index ac29c662..49d22385 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -165,6 +165,46 @@ class GCDAXI4BlackBoxRocketConfig extends Config( new freechips.rocketchip.system.BaseConfig) // 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( new chipyard.iobinders.WithUARTAdapter ++ new chipyard.iobinders.WithTieOffInterrupts ++ diff --git a/generators/testchipip b/generators/testchipip index 5b3f2c96..888a4547 160000 --- a/generators/testchipip +++ b/generators/testchipip @@ -1 +1 @@ -Subproject commit 5b3f2c9654d4cb06a64e8ac5df864c706a07bda8 +Subproject commit 888a4547adbf6387da7fb79c85f2f75cdcca6534 diff --git a/generators/utilities/src/main/resources/csrc/emulator.cc b/generators/utilities/src/main/resources/csrc/emulator.cc index acec6005..6ff63d3e 100644 --- a/generators/utilities/src/main/resources/csrc/emulator.cc +++ b/generators/utilities/src/main/resources/csrc/emulator.cc @@ -38,8 +38,8 @@ extern remote_bitbang_t * jtag; extern int dramsim; static uint64_t trace_count = 0; -bool verbose; -bool done_reset; +bool verbose = false; +bool done_reset = false; void handle_sigterm(int sig) { @@ -282,6 +282,10 @@ done_processing: signal(SIGTERM, handle_sigterm); 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 for (int i = 0; i < 100; i++) { tile->reset = 1; diff --git a/scripts/tutorial-patches/RocketConfigs.scala.patch b/scripts/tutorial-patches/RocketConfigs.scala.patch index eca5596d..af383b0f 100644 --- a/scripts/tutorial-patches/RocketConfigs.scala.patch +++ b/scripts/tutorial-patches/RocketConfigs.scala.patch @@ -1,8 +1,8 @@ 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 +++ 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.WithUART ++ new chipyard.config.WithL2TLBs(1024) ++ diff --git a/tests/.gitignore b/tests/.gitignore index b8212fd8..5cbdb5a9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,4 +1,5 @@ *.o *.riscv *.dump +*.img libgloss/ diff --git a/tests/Makefile b/tests/Makefile index 6f247e70..8754cbbc 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,12 +5,15 @@ LDFLAGS= -static 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 .PHONY: default -default: $(addsuffix .riscv,$(PROGRAMS)) +default: $(addsuffix .riscv,$(PROGRAMS)) spiflash.img .PHONY: dumps dumps: $(addsuffix .dump,$(PROGRAMS)) @@ -18,7 +21,7 @@ dumps: $(addsuffix .dump,$(PROGRAMS)) %.o: %.S $(GCC) $(CFLAGS) -D__ASSEMBLY__=1 -c $< -o $@ -%.o: %.c mmio.h +%.o: %.c mmio.h spiflash.h $(GCC) $(CFLAGS) -c $< -o $@ %.riscv: %.o $(libgloss) diff --git a/tests/spiflash.h b/tests/spiflash.h new file mode 100644 index 00000000..94b27e36 --- /dev/null +++ b/tests/spiflash.h @@ -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__ */ diff --git a/tests/spiflash.py b/tests/spiflash.py new file mode 100755 index 00000000..126cf1be --- /dev/null +++ b/tests/spiflash.py @@ -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')) + diff --git a/tests/spiflashread.c b/tests/spiflashread.c new file mode 100644 index 00000000..393faa55 --- /dev/null +++ b/tests/spiflashread.c @@ -0,0 +1,77 @@ +#include +#include + +#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; + +} diff --git a/tests/spiflashwrite.c b/tests/spiflashwrite.c new file mode 100644 index 00000000..f9c17605 --- /dev/null +++ b/tests/spiflashwrite.c @@ -0,0 +1,55 @@ +#include +#include + +#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; +}