From 59c9a162631b78eff84e2002478413b98c2cffbb Mon Sep 17 00:00:00 2001 From: Yann Herklotz Date: Thu, 24 Nov 2022 22:27:15 +0000 Subject: Move all files --- picosoc/.gitignore | 29 ++ picosoc/Makefile | 123 ++++++++ picosoc/README.md | 114 +++++++ picosoc/firmware.c | 770 ++++++++++++++++++++++++++++++++++++++++++++++ picosoc/hx8kdemo.core | 35 +++ picosoc/hx8kdemo.pcf | 40 +++ picosoc/hx8kdemo.v | 139 +++++++++ picosoc/hx8kdemo_tb.v | 108 +++++++ picosoc/ice40up5k_spram.v | 91 ++++++ picosoc/icebreaker.core | 36 +++ picosoc/icebreaker.pcf | 25 ++ picosoc/icebreaker.v | 151 +++++++++ picosoc/icebreaker_tb.v | 122 ++++++++ picosoc/overview.svg | 757 +++++++++++++++++++++++++++++++++++++++++++++ picosoc/performance.png | Bin 0 -> 62502 bytes picosoc/performance.py | 165 ++++++++++ picosoc/picosoc.core | 27 ++ picosoc/picosoc.v | 262 ++++++++++++++++ picosoc/sections.lds | 71 +++++ picosoc/simpleuart.v | 137 +++++++++ picosoc/spiflash.core | 24 ++ picosoc/spiflash.v | 409 ++++++++++++++++++++++++ picosoc/spiflash_tb.v | 366 ++++++++++++++++++++++ picosoc/spimemio.v | 579 ++++++++++++++++++++++++++++++++++ picosoc/start.s | 159 ++++++++++ 25 files changed, 4739 insertions(+) create mode 100644 picosoc/.gitignore create mode 100644 picosoc/Makefile create mode 100644 picosoc/README.md create mode 100644 picosoc/firmware.c create mode 100644 picosoc/hx8kdemo.core create mode 100644 picosoc/hx8kdemo.pcf create mode 100644 picosoc/hx8kdemo.v create mode 100644 picosoc/hx8kdemo_tb.v create mode 100644 picosoc/ice40up5k_spram.v create mode 100644 picosoc/icebreaker.core create mode 100644 picosoc/icebreaker.pcf create mode 100644 picosoc/icebreaker.v create mode 100644 picosoc/icebreaker_tb.v create mode 100644 picosoc/overview.svg create mode 100644 picosoc/performance.png create mode 100644 picosoc/performance.py create mode 100644 picosoc/picosoc.core create mode 100644 picosoc/picosoc.v create mode 100644 picosoc/sections.lds create mode 100644 picosoc/simpleuart.v create mode 100644 picosoc/spiflash.core create mode 100644 picosoc/spiflash.v create mode 100644 picosoc/spiflash_tb.v create mode 100644 picosoc/spimemio.v create mode 100644 picosoc/start.s (limited to 'picosoc') diff --git a/picosoc/.gitignore b/picosoc/.gitignore new file mode 100644 index 0000000..4ac8239 --- /dev/null +++ b/picosoc/.gitignore @@ -0,0 +1,29 @@ +/spiflash_tb.vcd +/spiflash_tb.vvp +/hx8kdemo.asc +/hx8kdemo.bin +/hx8kdemo.json +/hx8kdemo.log +/hx8kdemo.rpt +/hx8kdemo_syn.v +/hx8kdemo_syn_tb.vvp +/hx8kdemo_tb.vvp +/hx8kdemo_fw.elf +/hx8kdemo_fw.hex +/hx8kdemo_fw.bin +/hx8kdemo_sections.lds +/icebreaker.asc +/icebreaker.bin +/icebreaker.json +/icebreaker.log +/icebreaker.rpt +/icebreaker_syn.v +/icebreaker_syn_tb.vvp +/icebreaker_tb.vvp +/icebreaker_fw.elf +/icebreaker_fw.hex +/icebreaker_fw.bin +/icebreaker_sections.lds +/testbench.vcd +/cmos.log + diff --git a/picosoc/Makefile b/picosoc/Makefile new file mode 100644 index 0000000..291a12d --- /dev/null +++ b/picosoc/Makefile @@ -0,0 +1,123 @@ + +CROSS=riscv32-unknown-elf- +CFLAGS= + +# ---- iCE40 HX8K Breakout Board ---- + +hx8ksim: hx8kdemo_tb.vvp hx8kdemo_fw.hex + vvp -N $< +firmware=hx8kdemo_fw.hex + +hx8ksynsim: hx8kdemo_syn_tb.vvp hx8kdemo_fw.hex + vvp -N $< +firmware=hx8kdemo_fw.hex + +hx8kdemo.json: hx8kdemo.v spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -json hx8kdemo.json' $^ + +hx8kdemo_tb.vvp: hx8kdemo_tb.v hx8kdemo.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +hx8kdemo_syn_tb.vvp: hx8kdemo_tb.v hx8kdemo_syn.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +hx8kdemo_syn.v: hx8kdemo.json + yosys -p 'read_json hx8kdemo.json; write_verilog hx8kdemo_syn.v' + +hx8kdemo.asc: hx8kdemo.pcf hx8kdemo.json + nextpnr-ice40 --hx8k --package ct256 --asc hx8kdemo.asc --json hx8kdemo.json --pcf hx8kdemo.pcf + +hx8kdemo.bin: hx8kdemo.asc + icetime -d hx8k -c 12 -mtr hx8kdemo.rpt hx8kdemo.asc + icepack hx8kdemo.asc hx8kdemo.bin + +hx8kprog: hx8kdemo.bin hx8kdemo_fw.bin + iceprog hx8kdemo.bin + iceprog -o 1M hx8kdemo_fw.bin + +hx8kprog_fw: hx8kdemo_fw.bin + iceprog -o 1M hx8kdemo_fw.bin + +hx8kdemo_sections.lds: sections.lds + $(CROSS)cpp -P -DHX8KDEMO -o $@ $^ + +hx8kdemo_fw.elf: hx8kdemo_sections.lds start.s firmware.c + $(CROSS)gcc $(CFLAGS) -DHX8KDEMO -mabi=ilp32 -march=rv32imc -Wl,--build-id=none,-Bstatic,-T,hx8kdemo_sections.lds,--strip-debug -ffreestanding -nostdlib -o hx8kdemo_fw.elf start.s firmware.c + +hx8kdemo_fw.hex: hx8kdemo_fw.elf + $(CROSS)objcopy -O verilog hx8kdemo_fw.elf hx8kdemo_fw.hex + +hx8kdemo_fw.bin: hx8kdemo_fw.elf + $(CROSS)objcopy -O binary hx8kdemo_fw.elf hx8kdemo_fw.bin + +# ---- iCE40 IceBreaker Board ---- + +icebsim: icebreaker_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +icebsynsim: icebreaker_syn_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +icebreaker.json: icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -ql icebreaker.log -p 'synth_ice40 -dsp -top icebreaker -json icebreaker.json' $^ + +icebreaker_tb.vvp: icebreaker_tb.v icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +icebreaker_syn_tb.vvp: icebreaker_tb.v icebreaker_syn.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +icebreaker_syn.v: icebreaker.json + yosys -p 'read_json icebreaker.json; write_verilog icebreaker_syn.v' + +icebreaker.asc: icebreaker.pcf icebreaker.json + nextpnr-ice40 --freq 13 --up5k --package sg48 --asc icebreaker.asc --pcf icebreaker.pcf --json icebreaker.json + +icebreaker.bin: icebreaker.asc + icetime -d up5k -c 12 -mtr icebreaker.rpt icebreaker.asc + icepack icebreaker.asc icebreaker.bin + +icebprog: icebreaker.bin icebreaker_fw.bin + iceprog icebreaker.bin + iceprog -o 1M icebreaker_fw.bin + +icebprog_fw: icebreaker_fw.bin + iceprog -o 1M icebreaker_fw.bin + +icebreaker_sections.lds: sections.lds + $(CROSS)cpp -P -DICEBREAKER -o $@ $^ + +icebreaker_fw.elf: icebreaker_sections.lds start.s firmware.c + $(CROSS)gcc $(CFLAGS) -DICEBREAKER -mabi=ilp32 -march=rv32ic -Wl,-Bstatic,-T,icebreaker_sections.lds,--strip-debug -ffreestanding -nostdlib -o icebreaker_fw.elf start.s firmware.c + +icebreaker_fw.hex: icebreaker_fw.elf + $(CROSS)objcopy -O verilog icebreaker_fw.elf icebreaker_fw.hex + +icebreaker_fw.bin: icebreaker_fw.elf + $(CROSS)objcopy -O binary icebreaker_fw.elf icebreaker_fw.bin + +# ---- Testbench for SPI Flash Model ---- + +spiflash_tb: spiflash_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +spiflash_tb.vvp: spiflash.v spiflash_tb.v + iverilog -s testbench -o $@ $^ + +# ---- ASIC Synthesis Tests ---- + +cmos.log: spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -l cmos.log -p 'synth -top picosoc; abc -g cmos2; opt -fast; stat' $^ + +# ---- Clean ---- + +clean: + rm -f testbench.vvp testbench.vcd spiflash_tb.vvp spiflash_tb.vcd + rm -f hx8kdemo_fw.elf hx8kdemo_fw.hex hx8kdemo_fw.bin cmos.log + rm -f icebreaker_fw.elf icebreaker_fw.hex icebreaker_fw.bin + rm -f hx8kdemo.blif hx8kdemo.log hx8kdemo.asc hx8kdemo.rpt hx8kdemo.bin + rm -f hx8kdemo_syn.v hx8kdemo_syn_tb.vvp hx8kdemo_tb.vvp + rm -f icebreaker.json icebreaker.log icebreaker.asc icebreaker.rpt icebreaker.bin + rm -f icebreaker_syn.v icebreaker_syn_tb.vvp icebreaker_tb.vvp + +.PHONY: spiflash_tb clean +.PHONY: hx8kprog hx8kprog_fw hx8ksim hx8ksynsim +.PHONY: icebprog icebprog_fw icebsim icebsynsim diff --git a/picosoc/README.md b/picosoc/README.md new file mode 100644 index 0000000..1708709 --- /dev/null +++ b/picosoc/README.md @@ -0,0 +1,114 @@ + +PicoSoC - A simple example SoC using PicoRV32 +============================================= + +![](overview.svg) + +This is a simple PicoRV32 example design that can run code directly from an SPI +flash chip. It can be used as a turn-key solution for simple control tasks in +ASIC and FPGA designs. + +An example implementation targeting the Lattice iCE40-HX8K Breakout Board is +included. + +The flash is mapped to the memory regions starting at 0x00000000 and +0x01000000, with the SRAM overlayed for the mapping at 0x00000000. The SRAM +is just a small scratchpad memory (default 256 words, i.e. 1 kB). + +The reset vector is set to 0x00100000, i.e. at 1MB into in the flash memory. + +See the included demo firmware and linker script for how to build a firmware +image for this system. + +Run `make hx8ksim` or `make icebsim` to run the test bench (and create `testbench.vcd`). + +Run `make hx8kprog` to build the configuration bit-stream and firmware images +and upload them to a connected iCE40-HX8K Breakout Board. + +Run `make icebprog` to build the configuration bit-stream and firmware images +and upload them to a connected iCEBreaker Board. + +| File | Description | +| ----------------------------------- | --------------------------------------------------------------- | +| [picosoc.v](picosoc.v) | Top-level PicoSoC Verilog module | +| [spimemio.v](spimemio.v) | Memory controller that interfaces to external SPI flash | +| [simpleuart.v](simpleuart.v) | Simple UART core connected directly to SoC TX/RX lines | +| [start.s](start.s) | Assembler source for firmware.hex/firmware.bin | +| [firmware.c](firmware.c) | C source for firmware.hex/firmware.bin | +| [sections.lds](sections.lds) | Linker script for firmware.hex/firmware.bin | +| [hx8kdemo.v](hx8kdemo.v) | FPGA-based example implementation on iCE40-HX8K Breakout Board | +| [hx8kdemo.pcf](hx8kdemo.pcf) | Pin constraints for implementation on iCE40-HX8K Breakout Board | +| [hx8kdemo\_tb.v](hx8kdemo_tb.v) | Testbench for implementation on iCE40-HX8K Breakout Board | +| [icebreaker.v](icebreaker.v) | FPGA-based example implementation on iCEBreaker Board | +| [icebreaker.pcf](icebreaker.pcf) | Pin constraints for implementation on iCEBreaker Board | +| [icebreaker\_tb.v](icebreaker_tb.v) | Testbench for implementation on iCEBreaker Board | + +### Memory map: + +| Address Range | Description | +| ------------------------ | --------------------------------------- | +| 0x00000000 .. 0x00FFFFFF | Internal SRAM | +| 0x01000000 .. 0x01FFFFFF | External Serial Flash | +| 0x02000000 .. 0x02000003 | SPI Flash Controller Config Register | +| 0x02000004 .. 0x02000007 | UART Clock Divider Register | +| 0x02000008 .. 0x0200000B | UART Send/Recv Data Register | +| 0x03000000 .. 0xFFFFFFFF | Memory mapped user peripherals | + +Reading from the addresses in the internal SRAM region beyond the end of the +physical SRAM will read from the corresponding addresses in serial flash. + +Reading from the UART Send/Recv Data Register will return the last received +byte, or -1 (all 32 bits set) when the receive buffer is empty. + +The UART Clock Divider Register must be set to the system clock frequency +divided by the baud rate. + +The example design (hx8kdemo.v) has the 8 LEDs on the iCE40-HX8K Breakout Board +mapped to the low byte of the 32 bit word at address 0x03000000. + +### SPI Flash Controller Config Register: + +| Bit(s) | Description | +| -----: | --------------------------------------------------------- | +| 31 | MEMIO Enable (reset=1, set to 0 to bit bang SPI commands) | +| 30:23 | Reserved (read 0) | +| 22 | DDR Enable bit (reset=0) | +| 21 | QSPI Enable bit (reset=0) | +| 20 | CRM Enable bit (reset=0) | +| 19:16 | Read latency (dummy) cycles (reset=8) | +| 15:12 | Reserved (read 0) | +| 11:8 | IO Output enable bits in bit bang mode | +| 7:6 | Reserved (read 0) | +| 5 | Chip select (CS) line in bit bang mode | +| 4 | Serial clock line in bit bang mode | +| 3:0 | IO data bits in bit bang mode | + +The following settings for CRM/DDR/QSPI modes are valid: + +| CRM | QSPI | DDR | Read Command Byte | Mode Byte | +| :-: | :--: | :-: | :-------------------- | :-------: | +| 0 | 0 | 0 | 03h Read | N/A | +| 0 | 0 | 1 | BBh Dual I/O Read | FFh | +| 1 | 0 | 1 | BBh Dual I/O Read | A5h | +| 0 | 1 | 0 | EBh Quad I/O Read | FFh | +| 1 | 1 | 0 | EBh Quad I/O Read | A5h | +| 0 | 1 | 1 | EDh DDR Quad I/O Read | FFh | +| 1 | 1 | 1 | EDh DDR Quad I/O Read | A5h | + +The following plot visualizes the relative performance of the different configurations: + +![](performance.png) + +Consult the datasheet for your SPI flash to learn which configurations are supported +by the chip and what the maximum clock frequencies are for each configuration. + +For Quad I/O mode the QUAD flag in CR1V must be set before enabling Quad I/O in the +SPI master. Either set it by writing the corresponding bit in CR1NV once, or by writing +it from your device firmware at every bootup. (See `set_flash_qspi_flag()` in +`firmware.c` for an example for the latter.) + +Note that some changes to the Lattice iCE40-HX8K Breakout Board are required to support +the faster configurations: (1) The flash chip must be replaced with one that supports the +faster read commands and (2) the IO2 and IO3 pins on the flash chip must be connected to +the FPGA IO pins T9 and T8 (near the center of J3). + diff --git a/picosoc/firmware.c b/picosoc/firmware.c new file mode 100644 index 0000000..aadf31b --- /dev/null +++ b/picosoc/firmware.c @@ -0,0 +1,770 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include + +#ifdef ICEBREAKER +# define MEM_TOTAL 0x20000 /* 128 KB */ +#elif HX8KDEMO +# define MEM_TOTAL 0x200 /* 2 KB */ +#else +# error "Set -DICEBREAKER or -DHX8KDEMO when compiling firmware.c" +#endif + +// a pointer to this is a null pointer, but the compiler does not +// know that because "sram" is a linker symbol from sections.lds. +extern uint32_t sram; + +#define reg_spictrl (*(volatile uint32_t*)0x02000000) +#define reg_uart_clkdiv (*(volatile uint32_t*)0x02000004) +#define reg_uart_data (*(volatile uint32_t*)0x02000008) +#define reg_leds (*(volatile uint32_t*)0x03000000) + +// -------------------------------------------------------- + +extern uint32_t flashio_worker_begin; +extern uint32_t flashio_worker_end; + +void flashio(uint8_t *data, int len, uint8_t wrencmd) +{ + uint32_t func[&flashio_worker_end - &flashio_worker_begin]; + + uint32_t *src_ptr = &flashio_worker_begin; + uint32_t *dst_ptr = func; + + while (src_ptr != &flashio_worker_end) + *(dst_ptr++) = *(src_ptr++); + + ((void(*)(uint8_t*, uint32_t, uint32_t))func)(data, len, wrencmd); +} + +#ifdef HX8KDEMO +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + uint32_t addr_cr1v = 0x800002; + + // Read Any Register (RDAR 65h) + buffer[0] = 0x65; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = 0; // dummy + buffer[5] = 0; // rdata + flashio(buffer, 6, 0); + uint8_t cr1v = buffer[5]; + + // Write Enable (WREN 06h) + Write Any Register (WRAR 71h) + buffer[0] = 0x71; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = cr1v | 2; // Enable QSPI + flashio(buffer, 5, 0x06); +} + +void set_flash_latency(uint8_t value) +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | ((value & 15) << 16); + + uint32_t addr = 0x800004; + uint8_t buffer_wr[5] = {0x71, addr >> 16, addr >> 8, addr, 0x70 | value}; + flashio(buffer_wr, 5, 0x06); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; +} +#endif + +#ifdef ICEBREAKER +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + + // Read Configuration Registers (RDCR1 35h) + buffer[0] = 0x35; + buffer[1] = 0x00; // rdata + flashio(buffer, 2, 0); + uint8_t sr2 = buffer[1]; + + // Write Enable Volatile (50h) + Write Status Register 2 (31h) + buffer[0] = 0x31; + buffer[1] = sr2 | 2; // Enable QSPI + flashio(buffer, 2, 0x50); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00240000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00670000; +} + +void enable_flash_crm() +{ + reg_spictrl |= 0x00100000; +} +#endif + +// -------------------------------------------------------- + +void putchar(char c) +{ + if (c == '\n') + putchar('\r'); + reg_uart_data = c; +} + +void print(const char *p) +{ + while (*p) + putchar(*(p++)); +} + +void print_hex(uint32_t v, int digits) +{ + for (int i = 7; i >= 0; i--) { + char c = "0123456789abcdef"[(v >> (4*i)) & 15]; + if (c == '0' && i >= digits) continue; + putchar(c); + digits = i; + } +} + +void print_dec(uint32_t v) +{ + if (v >= 1000) { + print(">=1000"); + return; + } + + if (v >= 900) { putchar('9'); v -= 900; } + else if (v >= 800) { putchar('8'); v -= 800; } + else if (v >= 700) { putchar('7'); v -= 700; } + else if (v >= 600) { putchar('6'); v -= 600; } + else if (v >= 500) { putchar('5'); v -= 500; } + else if (v >= 400) { putchar('4'); v -= 400; } + else if (v >= 300) { putchar('3'); v -= 300; } + else if (v >= 200) { putchar('2'); v -= 200; } + else if (v >= 100) { putchar('1'); v -= 100; } + + if (v >= 90) { putchar('9'); v -= 90; } + else if (v >= 80) { putchar('8'); v -= 80; } + else if (v >= 70) { putchar('7'); v -= 70; } + else if (v >= 60) { putchar('6'); v -= 60; } + else if (v >= 50) { putchar('5'); v -= 50; } + else if (v >= 40) { putchar('4'); v -= 40; } + else if (v >= 30) { putchar('3'); v -= 30; } + else if (v >= 20) { putchar('2'); v -= 20; } + else if (v >= 10) { putchar('1'); v -= 10; } + + if (v >= 9) { putchar('9'); v -= 9; } + else if (v >= 8) { putchar('8'); v -= 8; } + else if (v >= 7) { putchar('7'); v -= 7; } + else if (v >= 6) { putchar('6'); v -= 6; } + else if (v >= 5) { putchar('5'); v -= 5; } + else if (v >= 4) { putchar('4'); v -= 4; } + else if (v >= 3) { putchar('3'); v -= 3; } + else if (v >= 2) { putchar('2'); v -= 2; } + else if (v >= 1) { putchar('1'); v -= 1; } + else putchar('0'); +} + +char getchar_prompt(char *prompt) +{ + int32_t c = -1; + + uint32_t cycles_begin, cycles_now, cycles; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + + reg_leds = ~0; + + if (prompt) + print(prompt); + + while (c == -1) { + __asm__ volatile ("rdcycle %0" : "=r"(cycles_now)); + cycles = cycles_now - cycles_begin; + if (cycles > 12000000) { + if (prompt) + print(prompt); + cycles_begin = cycles_now; + reg_leds = ~reg_leds; + } + c = reg_uart_data; + } + + reg_leds = 0; + return c; +} + +char getchar() +{ + return getchar_prompt(0); +} + +void cmd_print_spi_state() +{ + print("SPI State:\n"); + + print(" LATENCY "); + print_dec((reg_spictrl >> 16) & 15); + print("\n"); + + print(" DDR "); + if ((reg_spictrl & (1 << 22)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" QSPI "); + if ((reg_spictrl & (1 << 21)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" CRM "); + if ((reg_spictrl & (1 << 20)) != 0) + print("ON\n"); + else + print("OFF\n"); +} + +uint32_t xorshift32(uint32_t *state) +{ + /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */ + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + + return x; +} + +void cmd_memtest() +{ + int cyc_count = 5; + int stride = 256; + uint32_t state; + + volatile uint32_t *base_word = (uint32_t *) 0; + volatile uint8_t *base_byte = (uint8_t *) 0; + + print("Running memtest "); + + // Walk in stride increments, word access + for (int i = 1; i <= cyc_count; i++) { + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + *(base_word + word) = xorshift32(&state); + } + + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + if (*(base_word + word) != xorshift32(&state)) { + print(" ***FAILED WORD*** at "); + print_hex(4*word, 4); + print("\n"); + return; + } + } + + print("."); + } + + // Byte access + for (int byte = 0; byte < 128; byte++) { + *(base_byte + byte) = (uint8_t) byte; + } + + for (int byte = 0; byte < 128; byte++) { + if (*(base_byte + byte) != (uint8_t) byte) { + print(" ***FAILED BYTE*** at "); + print_hex(byte, 4); + print("\n"); + return; + } + } + + print(" passed\n"); +} + +// -------------------------------------------------------- + +void cmd_read_flash_id() +{ + uint8_t buffer[17] = { 0x9F, /* zeros */ }; + flashio(buffer, 17, 0); + + for (int i = 1; i <= 16; i++) { + putchar(' '); + print_hex(buffer[i], 2); + } + putchar('\n'); +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +uint8_t cmd_read_flash_regs_print(uint32_t addr, const char *name) +{ + set_flash_latency(8); + + uint8_t buffer[6] = {0x65, addr >> 16, addr >> 8, addr, 0, 0}; + flashio(buffer, 6, 0); + + print("0x"); + print_hex(addr, 6); + print(" "); + print(name); + print(" 0x"); + print_hex(buffer[5], 2); + print("\n"); + + return buffer[5]; +} + +void cmd_read_flash_regs() +{ + print("\n"); + uint8_t sr1v = cmd_read_flash_regs_print(0x800000, "SR1V"); + uint8_t sr2v = cmd_read_flash_regs_print(0x800001, "SR2V"); + uint8_t cr1v = cmd_read_flash_regs_print(0x800002, "CR1V"); + uint8_t cr2v = cmd_read_flash_regs_print(0x800003, "CR2V"); + uint8_t cr3v = cmd_read_flash_regs_print(0x800004, "CR3V"); + uint8_t vdlp = cmd_read_flash_regs_print(0x800005, "VDLP"); +} +#endif + +#ifdef ICEBREAKER +uint8_t cmd_read_flash_reg(uint8_t cmd) +{ + uint8_t buffer[2] = {cmd, 0}; + flashio(buffer, 2, 0); + return buffer[1]; +} + +void print_reg_bit(int val, const char *name) +{ + for (int i = 0; i < 12; i++) { + if (*name == 0) + putchar(' '); + else + putchar(*(name++)); + } + + putchar(val ? '1' : '0'); + putchar('\n'); +} + +void cmd_read_flash_regs() +{ + putchar('\n'); + + uint8_t sr1 = cmd_read_flash_reg(0x05); + uint8_t sr2 = cmd_read_flash_reg(0x35); + uint8_t sr3 = cmd_read_flash_reg(0x15); + + print_reg_bit(sr1 & 0x01, "S0 (BUSY)"); + print_reg_bit(sr1 & 0x02, "S1 (WEL)"); + print_reg_bit(sr1 & 0x04, "S2 (BP0)"); + print_reg_bit(sr1 & 0x08, "S3 (BP1)"); + print_reg_bit(sr1 & 0x10, "S4 (BP2)"); + print_reg_bit(sr1 & 0x20, "S5 (TB)"); + print_reg_bit(sr1 & 0x40, "S6 (SEC)"); + print_reg_bit(sr1 & 0x80, "S7 (SRP)"); + putchar('\n'); + + print_reg_bit(sr2 & 0x01, "S8 (SRL)"); + print_reg_bit(sr2 & 0x02, "S9 (QE)"); + print_reg_bit(sr2 & 0x04, "S10 ----"); + print_reg_bit(sr2 & 0x08, "S11 (LB1)"); + print_reg_bit(sr2 & 0x10, "S12 (LB2)"); + print_reg_bit(sr2 & 0x20, "S13 (LB3)"); + print_reg_bit(sr2 & 0x40, "S14 (CMP)"); + print_reg_bit(sr2 & 0x80, "S15 (SUS)"); + putchar('\n'); + + print_reg_bit(sr3 & 0x01, "S16 ----"); + print_reg_bit(sr3 & 0x02, "S17 ----"); + print_reg_bit(sr3 & 0x04, "S18 (WPS)"); + print_reg_bit(sr3 & 0x08, "S19 ----"); + print_reg_bit(sr3 & 0x10, "S20 ----"); + print_reg_bit(sr3 & 0x20, "S21 (DRV0)"); + print_reg_bit(sr3 & 0x40, "S22 (DRV1)"); + print_reg_bit(sr3 & 0x80, "S23 (HOLD)"); + putchar('\n'); +} +#endif + +// -------------------------------------------------------- + +uint32_t cmd_benchmark(bool verbose, uint32_t *instns_p) +{ + uint8_t data[256]; + uint32_t *words = (void*)data; + + uint32_t x32 = 314159265; + + uint32_t cycles_begin, cycles_end; + uint32_t instns_begin, instns_end; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_begin)); + + for (int i = 0; i < 20; i++) + { + for (int k = 0; k < 256; k++) + { + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + data[k] = x32; + } + + for (int k = 0, p = 0; k < 256; k++) + { + if (data[k]) + data[p++] = k; + } + + for (int k = 0, p = 0; k < 64; k++) + { + x32 = x32 ^ words[k]; + } + } + + __asm__ volatile ("rdcycle %0" : "=r"(cycles_end)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_end)); + + if (verbose) + { + print("Cycles: 0x"); + print_hex(cycles_end - cycles_begin, 8); + putchar('\n'); + + print("Instns: 0x"); + print_hex(instns_end - instns_begin, 8); + putchar('\n'); + + print("Chksum: 0x"); + print_hex(x32, 8); + putchar('\n'); + } + + if (instns_p) + *instns_p = instns_end - instns_begin; + + return cycles_end - cycles_begin; +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + for (int i = 8; i > 0; i--) + { + print("dspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("dspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00500000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00300000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00700000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + print("instns : "); + print_hex(instns, 8); + putchar('\n'); +} +#endif + +#ifdef ICEBREAKER +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + set_flash_mode_spi(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("dual "); + set_flash_mode_dual(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + // print("dual-crm "); + // enable_flash_crm(); + // print_hex(cmd_benchmark(false, &instns), 8); + // putchar('\n'); + + print("quad "); + set_flash_mode_quad(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("quad-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr "); + set_flash_mode_qddr(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + +} +#endif + +void cmd_echo() +{ + print("Return to menu by sending '!'\n\n"); + char c; + while ((c = getchar()) != '!') + putchar(c); +} + +// -------------------------------------------------------- + +void main() +{ + reg_leds = 31; + reg_uart_clkdiv = 104; + print("Booting..\n"); + + reg_leds = 63; + set_flash_qspi_flag(); + + reg_leds = 127; + while (getchar_prompt("Press ENTER to continue..\n") != '\r') { /* wait */ } + + print("\n"); + print(" ____ _ ____ ____\n"); + print(" | _ \\(_) ___ ___/ ___| ___ / ___|\n"); + print(" | |_) | |/ __/ _ \\___ \\ / _ \\| |\n"); + print(" | __/| | (_| (_) |__) | (_) | |___\n"); + print(" |_| |_|\\___\\___/____/ \\___/ \\____|\n"); + print("\n"); + + print("Total memory: "); + print_dec(MEM_TOTAL / 1024); + print(" KiB\n"); + print("\n"); + + //cmd_memtest(); // test overwrites bss and data memory + print("\n"); + + cmd_print_spi_state(); + print("\n"); + + while (1) + { + print("\n"); + + print("Select an action:\n"); + print("\n"); + print(" [1] Read SPI Flash ID\n"); + print(" [2] Read SPI Config Regs\n"); + print(" [3] Switch to default mode\n"); + print(" [4] Switch to Dual I/O mode\n"); + print(" [5] Switch to Quad I/O mode\n"); + print(" [6] Switch to Quad DDR mode\n"); + print(" [7] Toggle continuous read mode\n"); + print(" [9] Run simplistic benchmark\n"); + print(" [0] Benchmark all configs\n"); + print(" [M] Run Memtest\n"); + print(" [S] Print SPI state\n"); + print(" [e] Echo UART\n"); + print("\n"); + + for (int rep = 10; rep > 0; rep--) + { + print("Command> "); + char cmd = getchar(); + if (cmd > 32 && cmd < 127) + putchar(cmd); + print("\n"); + + switch (cmd) + { + case '1': + cmd_read_flash_id(); + break; + case '2': + cmd_read_flash_regs(); + break; + case '3': + set_flash_mode_spi(); + break; + case '4': + set_flash_mode_dual(); + break; + case '5': + set_flash_mode_quad(); + break; + case '6': + set_flash_mode_qddr(); + break; + case '7': + reg_spictrl = reg_spictrl ^ 0x00100000; + break; + case '9': + cmd_benchmark(true, 0); + break; + case '0': + cmd_benchmark_all(); + break; + case 'M': + cmd_memtest(); + break; + case 'S': + cmd_print_spi_state(); + break; + case 'e': + cmd_echo(); + break; + default: + continue; + } + + break; + } + } +} diff --git a/picosoc/hx8kdemo.core b/picosoc/hx8kdemo.core new file mode 100644 index 0000000..97a1989 --- /dev/null +++ b/picosoc/hx8kdemo.core @@ -0,0 +1,35 @@ +CAPI=2: + +name : ::hx8kdemo:0 + +filesets: + hx8kdemo: + files: [hx8kdemo.v] + file_type : verilogSource + depend : [picosoc] + hx8ksim: + files: + - hx8kdemo_tb.v + file_type : verilogSource + depend : [spiflash, "yosys:techlibs:ice40"] + + constraints: + files: [hx8kdemo.pcf] + file_type : PCF + +targets: + synth: + default_tool : icestorm + filesets : [constraints, hx8kdemo] + tools: + icestorm: + arachne_pnr_options : [-d, 8k] + toplevel : [hx8kdemo] + sim: + default_tool : icarus + filesets : [hx8kdemo, hx8ksim] + tools: + xsim: + xelab_options : [--timescale, 1ns/1ps] + + toplevel : [testbench] diff --git a/picosoc/hx8kdemo.pcf b/picosoc/hx8kdemo.pcf new file mode 100644 index 0000000..418bae8 --- /dev/null +++ b/picosoc/hx8kdemo.pcf @@ -0,0 +1,40 @@ + +# Pinout for the iCE40-HX8K Breakout Board + +set_io clk J3 + +set_io flash_csb R12 +set_io flash_clk R11 +set_io flash_io0 P12 +set_io flash_io1 P11 + +# for QSPI mode the flash chip on the iCE40-HX8K Breakout Board +# must be replaced with one that supports QSPI and the IO2 and IO3 +# pins must be soldered to T9 and P8 (center on J3) +set_io flash_io2 T9 +set_io flash_io3 P8 + +set_io ser_tx B12 +set_io ser_rx B10 + +# left on J3 +set_io debug_ser_tx T1 +set_io debug_ser_rx R3 + +# right on J3 +set_io debug_flash_csb T15 +set_io debug_flash_clk R16 +set_io debug_flash_io0 N12 +set_io debug_flash_io1 P13 +set_io debug_flash_io2 T13 +set_io debug_flash_io3 T14 + +set_io leds[7] B5 # D9 +set_io leds[6] B4 # D8 +set_io leds[5] A2 # D7 +set_io leds[4] A1 # D6 +set_io leds[3] C5 # D5 +set_io leds[2] C4 # D4 +set_io leds[1] B3 # D3 +set_io leds[0] C3 # D2 + diff --git a/picosoc/hx8kdemo.v b/picosoc/hx8kdemo.v new file mode 100644 index 0000000..f9a82f1 --- /dev/null +++ b/picosoc/hx8kdemo.v @@ -0,0 +1,139 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module hx8kdemo ( + input clk, + + output ser_tx, + input ser_rx, + + output [7:0] leds, + + output flash_csb, + output flash_clk, + inout flash_io0, + inout flash_io1, + inout flash_io2, + inout flash_io3, + + output debug_ser_tx, + output debug_ser_rx, + + output debug_flash_csb, + output debug_flash_clk, + output debug_flash_io0, + output debug_flash_io1, + output debug_flash_io2, + output debug_flash_io3 +); + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + wire flash_io0_oe, flash_io0_do, flash_io0_di; + wire flash_io1_oe, flash_io1_do, flash_io1_di; + wire flash_io2_oe, flash_io2_do, flash_io2_di; + wire flash_io3_oe, flash_io3_do, flash_io3_di; + + SB_IO #( + .PIN_TYPE(6'b 1010_01), + .PULLUP(1'b 0) + ) flash_io_buf [3:0] ( + .PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), + .OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), + .D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), + .D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) + ); + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + assign leds = gpio; + + always @(posedge clk) begin + if (!resetn) begin + gpio <= 0; + end else begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + end + + picosoc soc ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .irq_5 (1'b0 ), + .irq_6 (1'b0 ), + .irq_7 (1'b0 ), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) + ); + + assign debug_ser_tx = ser_tx; + assign debug_ser_rx = ser_rx; + + assign debug_flash_csb = flash_csb; + assign debug_flash_clk = flash_clk; + assign debug_flash_io0 = flash_io0_di; + assign debug_flash_io1 = flash_io1_di; + assign debug_flash_io2 = flash_io2_di; + assign debug_flash_io3 = flash_io3_di; +endmodule diff --git a/picosoc/hx8kdemo_tb.v b/picosoc/hx8kdemo_tb.v new file mode 100644 index 0000000..3fe9c1d --- /dev/null +++ b/picosoc/hx8kdemo_tb.v @@ -0,0 +1,108 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg clk; + always #5 clk = (clk === 1'b0); + + localparam ser_half_period = 53; + event ser_sample; + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + + repeat (6) begin + repeat (50000) @(posedge clk); + $display("+50000 cycles"); + end + $finish; + end + + integer cycle_cnt = 0; + + always @(posedge clk) begin + cycle_cnt <= cycle_cnt + 1; + end + + wire [7:0] leds; + + wire ser_rx; + wire ser_tx; + + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + always @(leds) begin + #1 $display("%b", leds); + end + + hx8kdemo uut ( + .clk (clk ), + .leds (leds ), + .ser_rx (ser_rx ), + .ser_tx (ser_tx ), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .flash_io2(flash_io2), + .flash_io3(flash_io3) + ); + + spiflash spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + reg [7:0] buffer; + + always begin + @(negedge ser_tx); + + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // start bit + + repeat (8) begin + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + buffer = {ser_tx, buffer[7:1]}; + -> ser_sample; // data bit + end + + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // stop bit + + if (buffer < 32 || buffer >= 127) + $display("Serial data: %d", buffer); + else + $display("Serial data: '%c'", buffer); + end +endmodule diff --git a/picosoc/ice40up5k_spram.v b/picosoc/ice40up5k_spram.v new file mode 100644 index 0000000..6d010f2 --- /dev/null +++ b/picosoc/ice40up5k_spram.v @@ -0,0 +1,91 @@ + +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module ice40up5k_spram #( + // We current always use the whole SPRAM (128 kB) + parameter integer WORDS = 32768 +) ( + input clk, + input [3:0] wen, + input [21:0] addr, + input [31:0] wdata, + output [31:0] rdata +); + + wire cs_0, cs_1; + wire [31:0] rdata_0, rdata_1; + + assign cs_0 = !addr[14]; + assign cs_1 = addr[14]; + assign rdata = addr[14] ? rdata_1 : rdata_0; + + SB_SPRAM256KA ram00 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[15:0]) + ); + + SB_SPRAM256KA ram01 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[31:16]) + ); + + SB_SPRAM256KA ram10 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[15:0]) + ); + + SB_SPRAM256KA ram11 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[31:16]) + ); + +endmodule diff --git a/picosoc/icebreaker.core b/picosoc/icebreaker.core new file mode 100644 index 0000000..5af5fd6 --- /dev/null +++ b/picosoc/icebreaker.core @@ -0,0 +1,36 @@ +CAPI=2: + +name : ::icebreaker:0 + +filesets: + top: + files: [icebreaker.v] + file_type : verilogSource + depend : [picosoc] + tb: + files: + - icebreaker_tb.v + file_type : verilogSource + depend : [spiflash, "yosys:techlibs:ice40"] + + constraints: + files: [icebreaker.pcf] + file_type : PCF + +targets: + synth: + default_tool : icestorm + filesets : [constraints, top] + tools: + icestorm: + nextpnr_options : [--freq, 13, --up5k] + pnr : next + toplevel : [icebreaker] + sim: + default_tool : icarus + filesets : [top, tb] + tools: + xsim: + xelab_options : [--timescale, 1ns/1ps] + + toplevel : [testbench] diff --git a/picosoc/icebreaker.pcf b/picosoc/icebreaker.pcf new file mode 100644 index 0000000..86cf78e --- /dev/null +++ b/picosoc/icebreaker.pcf @@ -0,0 +1,25 @@ +# 12 MHz clock +set_io clk 35 + +# RS232 +set_io ser_rx 6 +set_io ser_tx 9 + +# SPI Flash +set_io flash_clk 15 +set_io flash_csb 16 +set_io flash_io0 14 +set_io flash_io1 17 +set_io flash_io2 12 +set_io flash_io3 13 + +# LEDs (PMOD 2) +set_io led1 27 +set_io led2 25 +set_io led3 21 +set_io led4 23 +set_io led5 26 + +# Onboard LEDs +set_io ledr_n 11 +set_io ledg_n 37 diff --git a/picosoc/icebreaker.v b/picosoc/icebreaker.v new file mode 100644 index 0000000..a943bc6 --- /dev/null +++ b/picosoc/icebreaker.v @@ -0,0 +1,151 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`ifdef PICOSOC_V +`error "icebreaker.v must be read before picosoc.v!" +`endif + +`define PICOSOC_MEM ice40up5k_spram + +module icebreaker ( + input clk, + + output ser_tx, + input ser_rx, + + output led1, + output led2, + output led3, + output led4, + output led5, + + output ledr_n, + output ledg_n, + + output flash_csb, + output flash_clk, + inout flash_io0, + inout flash_io1, + inout flash_io2, + inout flash_io3 +); + parameter integer MEM_WORDS = 32768; + + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + wire [7:0] leds; + + assign led1 = leds[1]; + assign led2 = leds[2]; + assign led3 = leds[3]; + assign led4 = leds[4]; + assign led5 = leds[5]; + + assign ledr_n = !leds[6]; + assign ledg_n = !leds[7]; + + wire flash_io0_oe, flash_io0_do, flash_io0_di; + wire flash_io1_oe, flash_io1_do, flash_io1_di; + wire flash_io2_oe, flash_io2_do, flash_io2_di; + wire flash_io3_oe, flash_io3_do, flash_io3_di; + + SB_IO #( + .PIN_TYPE(6'b 1010_01), + .PULLUP(1'b 0) + ) flash_io_buf [3:0] ( + .PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), + .OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), + .D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), + .D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) + ); + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + assign leds = gpio; + + always @(posedge clk) begin + if (!resetn) begin + gpio <= 0; + end else begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + end + + picosoc #( + .BARREL_SHIFTER(0), + .ENABLE_MUL(0), + .ENABLE_DIV(0), + .ENABLE_FAST_MUL(1), + .MEM_WORDS(MEM_WORDS) + ) soc ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .irq_5 (1'b0 ), + .irq_6 (1'b0 ), + .irq_7 (1'b0 ), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) + ); +endmodule diff --git a/picosoc/icebreaker_tb.v b/picosoc/icebreaker_tb.v new file mode 100644 index 0000000..c09c10d --- /dev/null +++ b/picosoc/icebreaker_tb.v @@ -0,0 +1,122 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg clk; + always #5 clk = (clk === 1'b0); + + localparam ser_half_period = 53; + event ser_sample; + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + + repeat (6) begin + repeat (50000) @(posedge clk); + $display("+50000 cycles"); + end + $finish; + end + + integer cycle_cnt = 0; + + always @(posedge clk) begin + cycle_cnt <= cycle_cnt + 1; + end + + wire led1, led2, led3, led4, led5; + wire ledr_n, ledg_n; + + wire [6:0] leds = {!ledg_n, !ledr_n, led5, led4, led3, led2, led1}; + + wire ser_rx; + wire ser_tx; + + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + always @(leds) begin + #1 $display("%b", leds); + end + + icebreaker #( + // We limit the amount of memory in simulation + // in order to avoid reduce simulation time + // required for intialization of RAM + .MEM_WORDS(256) + ) uut ( + .clk (clk ), + .led1 (led1 ), + .led2 (led2 ), + .led3 (led3 ), + .led4 (led4 ), + .led5 (led5 ), + .ledr_n (ledr_n ), + .ledg_n (ledg_n ), + .ser_rx (ser_rx ), + .ser_tx (ser_tx ), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .flash_io2(flash_io2), + .flash_io3(flash_io3) + ); + + spiflash spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + reg [7:0] buffer; + + always begin + @(negedge ser_tx); + + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // start bit + + repeat (8) begin + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + buffer = {ser_tx, buffer[7:1]}; + -> ser_sample; // data bit + end + + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // stop bit + + if (buffer < 32 || buffer >= 127) + $display("Serial data: %d", buffer); + else + $display("Serial data: '%c'", buffer); + end +endmodule diff --git a/picosoc/overview.svg b/picosoc/overview.svg new file mode 100644 index 0000000..2335f76 --- /dev/null +++ b/picosoc/overview.svg @@ -0,0 +1,757 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + Your Chip + + PicoSoC + + XIP SPIFlashController + + UART + + SRAM(default 1kB) + 32 Bit System Bus + + PicoRV32 + + + + + + + + + + + + + FLASH_SCK + FLASH_CSB + FLASH_IO0 + FLASH_IO1 + FLASH_IO2 + FLASH_IO3 + SERIAL_TX + SERIAL_RX + + + + + + Your Circuit + SYSTEM_CLOCK + SYSTEM_RESET + + + GPIO Controller + + Custom Block + + Custom Block + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - RISC-V ISA- RV32IMC- GNU Toolchain + + + + + + + + + + + + + + + + + + + + diff --git a/picosoc/performance.png b/picosoc/performance.png new file mode 100644 index 0000000..aac75b2 Binary files /dev/null and b/picosoc/performance.png differ diff --git a/picosoc/performance.py b/picosoc/performance.py new file mode 100644 index 0000000..92c50c5 --- /dev/null +++ b/picosoc/performance.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +import matplotlib.pyplot as plt +import numpy as np + +uncompr_text = """ +default : 010f52ef +dspi-8 : 008dc82f +dspi-7 : 008d6d63 +dspi-6 : 008d1297 +dspi-5 : 008cb7cb +dspi-4 : 008c5cff +dspi-3 : 008c0233 +dspi-2 : 008ba767 +dspi-1 : 008b4c9b +dspi-crm-8 : 008af1cf +dspi-crm-7 : 008a9703 +dspi-crm-6 : 008a3c37 +dspi-crm-5 : 0089e16b +dspi-crm-4 : 0089869f +dspi-crm-3 : 00892bd3 +dspi-crm-2 : 0088d107 +dspi-crm-1 : 0088763b +qspi-8 : 004a2c6f +qspi-7 : 0049d1a3 +qspi-6 : 004976d7 +qspi-5 : 00491c0b +qspi-4 : 0048c13f +qspi-3 : 00486673 +qspi-2 : 00480ba7 +qspi-1 : 0047b0db +qspi-crm-8 : 0047560f +qspi-crm-7 : 0046fb43 +qspi-crm-6 : 0046a077 +qspi-crm-5 : 004645ab +qspi-crm-4 : 0045eadf +qspi-crm-3 : 00459013 +qspi-crm-2 : 00453547 +qspi-crm-1 : 0044da7b +qspi-ddr-8 : 00288bf5 +qspi-ddr-7 : 00283129 +qspi-ddr-6 : 0027d65d +qspi-ddr-5 : 00277b91 +qspi-ddr-4 : 002720c5 +qspi-ddr-3 : 0026c5f9 +qspi-ddr-2 : 00266b2d +qspi-ddr-1 : 00261061 +qspi-ddr-crm-8 : 0025b595 +qspi-ddr-crm-7 : 00255ac9 +qspi-ddr-crm-6 : 0024fffd +qspi-ddr-crm-5 : 0024a531 +qspi-ddr-crm-4 : 00244a65 +qspi-ddr-crm-3 : 0023ef99 +qspi-ddr-crm-2 : 002394cd +qspi-ddr-crm-1 : 00233a01 +instns : 0003df2d +""" + +compr_text = """ +default : 00f3d36d +dspi-8 : 008008ad +dspi-7 : 007fade1 +dspi-6 : 007f5315 +dspi-5 : 007ef849 +dspi-4 : 007e9d7d +dspi-3 : 007e42b1 +dspi-2 : 007de7e5 +dspi-1 : 007d8d19 +dspi-crm-8 : 007d324d +dspi-crm-7 : 007cd781 +dspi-crm-6 : 007c7cb5 +dspi-crm-5 : 007c21e9 +dspi-crm-4 : 007bc71d +dspi-crm-3 : 007b6c51 +dspi-crm-2 : 007b1185 +dspi-crm-1 : 007ab6b9 +qspi-8 : 00434ced +qspi-7 : 0042f221 +qspi-6 : 00429755 +qspi-5 : 00423c89 +qspi-4 : 0041e1bd +qspi-3 : 004186f1 +qspi-2 : 00412c25 +qspi-1 : 0040d159 +qspi-crm-8 : 0040768d +qspi-crm-7 : 00401bc1 +qspi-crm-6 : 003fc0f5 +qspi-crm-5 : 003f6629 +qspi-crm-4 : 003f0b5d +qspi-crm-3 : 003eb091 +qspi-crm-2 : 003e55c5 +qspi-crm-1 : 003dfaf9 +qspi-ddr-8 : 00255d87 +qspi-ddr-7 : 002502bb +qspi-ddr-6 : 0024a7ef +qspi-ddr-5 : 00244d23 +qspi-ddr-4 : 0023f257 +qspi-ddr-3 : 0023978b +qspi-ddr-2 : 00233cbf +qspi-ddr-1 : 0022e1f3 +qspi-ddr-crm-8 : 00228727 +qspi-ddr-crm-7 : 00222c5b +qspi-ddr-crm-6 : 0021d18f +qspi-ddr-crm-5 : 002176c3 +qspi-ddr-crm-4 : 00211bf7 +qspi-ddr-crm-3 : 0020c12b +qspi-ddr-crm-2 : 0020665f +qspi-ddr-crm-1 : 00200b93 +instns : 0003df2d +""" + +labels = list() +uncompr_values = list() +compr_values = list() + +for line in uncompr_text.split("\n"): + if line != "": + line = line.split() + if line[0] == "instns": + for i in range(len(uncompr_values)): + uncompr_values[i] = int(line[2], 16) / uncompr_values[i] + else: + labels.append(line[0]) + uncompr_values.append(int(line[2], 16)) + +for line in compr_text.split("\n"): + if line != "": + line = line.split() + if line[0] == "instns": + for i in range(len(compr_values)): + compr_values[i] = int(line[2], 16) / compr_values[i] + else: + compr_values.append(int(line[2], 16)) + +print(np.array(compr_values) / np.array(uncompr_values)) + +values = list() +for i in range(len(compr_values)): + values.append(uncompr_values[i] / uncompr_values[0]) + # values.append(compr_values[i] / compr_values[0]) + +values = np.array(values) +print(values) + +plt.figure(figsize=(10, 5)) +plt.title("Performance comparison for different PicoSoC SPI flash configurations") +plt.plot(range(len(labels)), values) +plt.xticks(range(len(labels)), labels, rotation=80) + +for color, x1, x2 in [["black", 0, 0], ["red", 1, 8], ["green", 9, 16], + ["red", 17, 24], ["green", 25, 32], ["red", 33, 40], ["green", 41, 48]]: + for t in plt.axes().xaxis.get_ticklabels()[x1:x2+1]: + t.set_color(color) + plt.plot([x1, x1], [0, values[x1] - 0.2], color=color) + plt.plot([x2, x2], [0, values[x2] - 0.2], color=color) + plt.plot([x1], [values[x1]], "k.") + plt.plot([x2], [values[x2]], "k.") + +plt.xlim(-1, len(labels)) +plt.ylim(0, 9) +plt.grid() + +plt.gcf().subplots_adjust(bottom=0.3) +plt.savefig("performance.png") +# plt.show() diff --git a/picosoc/picosoc.core b/picosoc/picosoc.core new file mode 100644 index 0000000..eb0988a --- /dev/null +++ b/picosoc/picosoc.core @@ -0,0 +1,27 @@ +CAPI=2: + +name : ::picosoc:0 + +filesets: + picosoc: + files: + - simpleuart.v + - spimemio.v + - picosoc.v + file_type : verilogSource + depend : [picorv32] + +targets: + default: + filesets : [picosoc] + parameters : [PICORV32_REGS, PICOSOC_MEM] + +parameters: + PICORV32_REGS: + datatype : str + default : picosoc_regs + paramtype : vlogdefine + PICOSOC_MEM: + datatype : str + default : picosoc_mem + paramtype : vlogdefine diff --git a/picosoc/picosoc.v b/picosoc/picosoc.v new file mode 100644 index 0000000..9790791 --- /dev/null +++ b/picosoc/picosoc.v @@ -0,0 +1,262 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`ifndef PICORV32_REGS +`ifdef PICORV32_V +`error "picosoc.v must be read before picorv32.v!" +`endif + +`define PICORV32_REGS picosoc_regs +`endif + +`ifndef PICOSOC_MEM +`define PICOSOC_MEM picosoc_mem +`endif + +// this macro can be used to check if the verilog files in your +// design are read in the correct order. +`define PICOSOC_V + +module picosoc ( + input clk, + input resetn, + + output iomem_valid, + input iomem_ready, + output [ 3:0] iomem_wstrb, + output [31:0] iomem_addr, + output [31:0] iomem_wdata, + input [31:0] iomem_rdata, + + input irq_5, + input irq_6, + input irq_7, + + output ser_tx, + input ser_rx, + + output flash_csb, + output flash_clk, + + output flash_io0_oe, + output flash_io1_oe, + output flash_io2_oe, + output flash_io3_oe, + + output flash_io0_do, + output flash_io1_do, + output flash_io2_do, + output flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di +); + parameter [0:0] BARREL_SHIFTER = 1; + parameter [0:0] ENABLE_MUL = 1; + parameter [0:0] ENABLE_DIV = 1; + parameter [0:0] ENABLE_FAST_MUL = 0; + parameter [0:0] ENABLE_COMPRESSED = 1; + parameter [0:0] ENABLE_COUNTERS = 1; + parameter [0:0] ENABLE_IRQ_QREGS = 0; + + parameter integer MEM_WORDS = 256; + parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory + parameter [31:0] PROGADDR_RESET = 32'h 0010_0000; // 1 MB into flash + parameter [31:0] PROGADDR_IRQ = 32'h 0000_0000; + + reg [31:0] irq; + wire irq_stall = 0; + wire irq_uart = 0; + + always @* begin + irq = 0; + irq[3] = irq_stall; + irq[4] = irq_uart; + irq[5] = irq_5; + irq[6] = irq_6; + irq[7] = irq_7; + end + + wire mem_valid; + wire mem_instr; + wire mem_ready; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + wire [31:0] mem_rdata; + + wire spimem_ready; + wire [31:0] spimem_rdata; + + reg ram_ready; + wire [31:0] ram_rdata; + + assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01); + assign iomem_wstrb = mem_wstrb; + assign iomem_addr = mem_addr; + assign iomem_wdata = mem_wdata; + + wire spimemio_cfgreg_sel = mem_valid && (mem_addr == 32'h 0200_0000); + wire [31:0] spimemio_cfgreg_do; + + wire simpleuart_reg_div_sel = mem_valid && (mem_addr == 32'h 0200_0004); + wire [31:0] simpleuart_reg_div_do; + + wire simpleuart_reg_dat_sel = mem_valid && (mem_addr == 32'h 0200_0008); + wire [31:0] simpleuart_reg_dat_do; + wire simpleuart_reg_dat_wait; + + assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready || spimemio_cfgreg_sel || + simpleuart_reg_div_sel || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait); + + assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata : + spimemio_cfgreg_sel ? spimemio_cfgreg_do : simpleuart_reg_div_sel ? simpleuart_reg_div_do : + simpleuart_reg_dat_sel ? simpleuart_reg_dat_do : 32'h 0000_0000; + + picorv32 #( + .STACKADDR(STACKADDR), + .PROGADDR_RESET(PROGADDR_RESET), + .PROGADDR_IRQ(PROGADDR_IRQ), + .BARREL_SHIFTER(BARREL_SHIFTER), + .COMPRESSED_ISA(ENABLE_COMPRESSED), + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_MUL(ENABLE_MUL), + .ENABLE_DIV(ENABLE_DIV), + .ENABLE_FAST_MUL(ENABLE_FAST_MUL), + .ENABLE_IRQ(1), + .ENABLE_IRQ_QREGS(ENABLE_IRQ_QREGS) + ) cpu ( + .clk (clk ), + .resetn (resetn ), + .mem_valid (mem_valid ), + .mem_instr (mem_instr ), + .mem_ready (mem_ready ), + .mem_addr (mem_addr ), + .mem_wdata (mem_wdata ), + .mem_wstrb (mem_wstrb ), + .mem_rdata (mem_rdata ), + .irq (irq ) + ); + + spimemio spimemio ( + .clk (clk), + .resetn (resetn), + .valid (mem_valid && mem_addr >= 4*MEM_WORDS && mem_addr < 32'h 0200_0000), + .ready (spimem_ready), + .addr (mem_addr[23:0]), + .rdata (spimem_rdata), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .cfgreg_we(spimemio_cfgreg_sel ? mem_wstrb : 4'b 0000), + .cfgreg_di(mem_wdata), + .cfgreg_do(spimemio_cfgreg_do) + ); + + simpleuart simpleuart ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .reg_div_we (simpleuart_reg_div_sel ? mem_wstrb : 4'b 0000), + .reg_div_di (mem_wdata), + .reg_div_do (simpleuart_reg_div_do), + + .reg_dat_we (simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0), + .reg_dat_re (simpleuart_reg_dat_sel && !mem_wstrb), + .reg_dat_di (mem_wdata), + .reg_dat_do (simpleuart_reg_dat_do), + .reg_dat_wait(simpleuart_reg_dat_wait) + ); + + always @(posedge clk) + ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS; + + `PICOSOC_MEM #( + .WORDS(MEM_WORDS) + ) memory ( + .clk(clk), + .wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0), + .addr(mem_addr[23:2]), + .wdata(mem_wdata), + .rdata(ram_rdata) + ); +endmodule + +// Implementation note: +// Replace the following two modules with wrappers for your SRAM cells. + +module picosoc_regs ( + input clk, wen, + input [5:0] waddr, + input [5:0] raddr1, + input [5:0] raddr2, + input [31:0] wdata, + output [31:0] rdata1, + output [31:0] rdata2 +); + reg [31:0] regs [0:31]; + + always @(posedge clk) + if (wen) regs[waddr[4:0]] <= wdata; + + assign rdata1 = regs[raddr1[4:0]]; + assign rdata2 = regs[raddr2[4:0]]; +endmodule + +module picosoc_mem #( + parameter integer WORDS = 256 +) ( + input clk, + input [3:0] wen, + input [21:0] addr, + input [31:0] wdata, + output reg [31:0] rdata +); + reg [31:0] mem [0:WORDS-1]; + + always @(posedge clk) begin + rdata <= mem[addr]; + if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0]; + if (wen[1]) mem[addr][15: 8] <= wdata[15: 8]; + if (wen[2]) mem[addr][23:16] <= wdata[23:16]; + if (wen[3]) mem[addr][31:24] <= wdata[31:24]; + end +endmodule + diff --git a/picosoc/sections.lds b/picosoc/sections.lds new file mode 100644 index 0000000..f38d813 --- /dev/null +++ b/picosoc/sections.lds @@ -0,0 +1,71 @@ +#ifdef ICEBREAKER +# define MEM_TOTAL 0x20000 /* 128 KB */ +#elif HX8KDEMO +# define MEM_TOTAL 0x200 /* 2 KB */ +#else +# error "Set -DICEBREAKER or -DHX8KDEMO when compiling firmware.c" +#endif + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00100000, LENGTH = 0x400000 /* entire flash, 4 MiB */ + RAM (xrw) : ORIGIN = 0x00000000, LENGTH = MEM_TOTAL +} + +SECTIONS { + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.srodata) /* .rodata sections (constants, strings, etc.) */ + *(.srodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + _etext = .; /* define a global symbol at end of code */ + _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ + } >FLASH + + + /* This is the initialized data section + The program executes knowing that the data is in the RAM + but the loader puts the initial values in the FLASH (inidata). + It is one task of the startup to copy the initial values from FLASH to RAM. */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + _ram_start = .; /* create a global symbol at ram start for garbage collector */ + . = ALIGN(4); + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.sdata) /* .sdata sections */ + *(.sdata*) /* .sdata* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + } >RAM + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* define a global symbol at bss start; used by startup code */ + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end; used by startup code */ + } >RAM + + /* this is to define the start of the heap, and make sure we have a minimum size */ + .heap : + { + . = ALIGN(4); + _heap_start = .; /* define a global symbol at heap start */ + } >RAM +} diff --git a/picosoc/simpleuart.v b/picosoc/simpleuart.v new file mode 100644 index 0000000..5ffef77 --- /dev/null +++ b/picosoc/simpleuart.v @@ -0,0 +1,137 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module simpleuart #(parameter integer DEFAULT_DIV = 1) ( + input clk, + input resetn, + + output ser_tx, + input ser_rx, + + input [3:0] reg_div_we, + input [31:0] reg_div_di, + output [31:0] reg_div_do, + + input reg_dat_we, + input reg_dat_re, + input [31:0] reg_dat_di, + output [31:0] reg_dat_do, + output reg_dat_wait +); + reg [31:0] cfg_divider; + + reg [3:0] recv_state; + reg [31:0] recv_divcnt; + reg [7:0] recv_pattern; + reg [7:0] recv_buf_data; + reg recv_buf_valid; + + reg [9:0] send_pattern; + reg [3:0] send_bitcnt; + reg [31:0] send_divcnt; + reg send_dummy; + + assign reg_div_do = cfg_divider; + + assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy); + assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; + + always @(posedge clk) begin + if (!resetn) begin + cfg_divider <= DEFAULT_DIV; + end else begin + if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0]; + if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8]; + if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16]; + if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24]; + end + end + + always @(posedge clk) begin + if (!resetn) begin + recv_state <= 0; + recv_divcnt <= 0; + recv_pattern <= 0; + recv_buf_data <= 0; + recv_buf_valid <= 0; + end else begin + recv_divcnt <= recv_divcnt + 1; + if (reg_dat_re) + recv_buf_valid <= 0; + case (recv_state) + 0: begin + if (!ser_rx) + recv_state <= 1; + recv_divcnt <= 0; + end + 1: begin + if (2*recv_divcnt > cfg_divider) begin + recv_state <= 2; + recv_divcnt <= 0; + end + end + 10: begin + if (recv_divcnt > cfg_divider) begin + recv_buf_data <= recv_pattern; + recv_buf_valid <= 1; + recv_state <= 0; + end + end + default: begin + if (recv_divcnt > cfg_divider) begin + recv_pattern <= {ser_rx, recv_pattern[7:1]}; + recv_state <= recv_state + 1; + recv_divcnt <= 0; + end + end + endcase + end + end + + assign ser_tx = send_pattern[0]; + + always @(posedge clk) begin + if (reg_div_we) + send_dummy <= 1; + send_divcnt <= send_divcnt + 1; + if (!resetn) begin + send_pattern <= ~0; + send_bitcnt <= 0; + send_divcnt <= 0; + send_dummy <= 1; + end else begin + if (send_dummy && !send_bitcnt) begin + send_pattern <= ~0; + send_bitcnt <= 15; + send_divcnt <= 0; + send_dummy <= 0; + end else + if (reg_dat_we && !send_bitcnt) begin + send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0}; + send_bitcnt <= 10; + send_divcnt <= 0; + end else + if (send_divcnt > cfg_divider && send_bitcnt) begin + send_pattern <= {1'b1, send_pattern[9:1]}; + send_bitcnt <= send_bitcnt - 1; + send_divcnt <= 0; + end + end + end +endmodule diff --git a/picosoc/spiflash.core b/picosoc/spiflash.core new file mode 100644 index 0000000..1b7d153 --- /dev/null +++ b/picosoc/spiflash.core @@ -0,0 +1,24 @@ +CAPI=2: + +name : ::spiflash:0 + +filesets: + model: + files : [spiflash.v] + file_type : verilogSource + tb: + files : [spiflash_tb.v] + file_type : verilogSource + +targets: + default: + default_tool : icarus + filesets : [model, "is_toplevel? (tb)"] + parameters : [firmware] + toplevel : [testbench] + +parameters : + firmware: + datatype : file + description : Initial SPI Flash contents (in verilog hex format) + paramtype : plusarg diff --git a/picosoc/spiflash.v b/picosoc/spiflash.v new file mode 100644 index 0000000..22b337b --- /dev/null +++ b/picosoc/spiflash.v @@ -0,0 +1,409 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +// +// Simple SPI flash simulation model +// +// This model samples io input signals 1ns before the SPI clock edge and +// updates output signals 1ns after the SPI clock edge. +// +// Supported commands: +// AB, B9, FF, 03, BB, EB, ED +// +// Well written SPI flash data sheets: +// Cypress S25FL064L http://www.cypress.com/file/316661/download +// Cypress S25FL128L http://www.cypress.com/file/316171/download +// +// SPI flash used on iCEBreaker board: +// https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf +// + +module spiflash ( + input csb, + input clk, + inout io0, // MOSI + inout io1, // MISO + inout io2, + inout io3 +); + localparam verbose = 0; + localparam integer latency = 8; + + reg [7:0] buffer; + integer bitcount = 0; + integer bytecount = 0; + integer dummycount = 0; + + reg [7:0] spi_cmd; + reg [7:0] xip_cmd = 0; + reg [23:0] spi_addr; + + reg [7:0] spi_in; + reg [7:0] spi_out; + reg spi_io_vld; + + reg powered_up = 0; + + localparam [3:0] mode_spi = 1; + localparam [3:0] mode_dspi_rd = 2; + localparam [3:0] mode_dspi_wr = 3; + localparam [3:0] mode_qspi_rd = 4; + localparam [3:0] mode_qspi_wr = 5; + localparam [3:0] mode_qspi_ddr_rd = 6; + localparam [3:0] mode_qspi_ddr_wr = 7; + + reg [3:0] mode = 0; + reg [3:0] next_mode = 0; + + reg io0_oe = 0; + reg io1_oe = 0; + reg io2_oe = 0; + reg io3_oe = 0; + + reg io0_dout = 0; + reg io1_dout = 0; + reg io2_dout = 0; + reg io3_dout = 0; + + assign #1 io0 = io0_oe ? io0_dout : 1'bz; + assign #1 io1 = io1_oe ? io1_dout : 1'bz; + assign #1 io2 = io2_oe ? io2_dout : 1'bz; + assign #1 io3 = io3_oe ? io3_dout : 1'bz; + + wire io0_delayed; + wire io1_delayed; + wire io2_delayed; + wire io3_delayed; + + assign #1 io0_delayed = io0; + assign #1 io1_delayed = io1; + assign #1 io2_delayed = io2; + assign #1 io3_delayed = io3; + + // 16 MB (128Mb) Flash + reg [7:0] memory [0:16*1024*1024-1]; + + reg [1023:0] firmware_file; + initial begin + if (!$value$plusargs("firmware=%s", firmware_file)) + firmware_file = "firmware.hex"; + $readmemh(firmware_file, memory); + end + + task spi_action; + begin + spi_in = buffer; + + if (bytecount == 1) begin + spi_cmd = buffer; + + if (spi_cmd == 8'h ab) + powered_up = 1; + + if (spi_cmd == 8'h b9) + powered_up = 0; + + if (spi_cmd == 8'h ff) + xip_cmd = 0; + end + + if (powered_up && spi_cmd == 'h 03) begin + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount >= 4) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h bb) begin + if (bytecount == 1) + mode = mode_dspi_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_dspi_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h eb) begin + if (bytecount == 1) + mode = mode_qspi_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_qspi_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h ed) begin + if (bytecount == 1) + next_mode = mode_qspi_ddr_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_qspi_ddr_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + spi_out = buffer; + spi_io_vld = 1; + + if (verbose) begin + if (bytecount == 1) + $write(""); + $write("", spi_in, spi_out); + end + + end + endtask + + task ddr_rd_edge; + begin + buffer = {buffer, io3_delayed, io2_delayed, io1_delayed, io0_delayed}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + endtask + + task ddr_wr_edge; + begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + + buffer = {buffer, 4'h 0}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + endtask + + always @(csb) begin + if (csb) begin + if (verbose) begin + $display(""); + $fflush; + end + buffer = 0; + bitcount = 0; + bytecount = 0; + mode = mode_spi; + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end else + if (xip_cmd) begin + buffer = xip_cmd; + bitcount = 0; + bytecount = 1; + spi_action; + end + end + + always @(csb, clk) begin + spi_io_vld = 0; + if (!csb && !clk) begin + if (dummycount > 0) begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end else + case (mode) + mode_spi: begin + io0_oe = 0; + io1_oe = 1; + io2_oe = 0; + io3_oe = 0; + io1_dout = buffer[7]; + end + mode_dspi_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_dspi_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 0; + io3_oe = 0; + io0_dout = buffer[6]; + io1_dout = buffer[7]; + end + mode_qspi_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_qspi_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + end + mode_qspi_ddr_rd: begin + ddr_rd_edge; + end + mode_qspi_ddr_wr: begin + ddr_wr_edge; + end + endcase + if (next_mode) begin + case (next_mode) + mode_qspi_ddr_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_qspi_ddr_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + end + endcase + mode = next_mode; + next_mode = 0; + end + end + end + + always @(posedge clk) begin + if (!csb) begin + if (dummycount > 0) begin + dummycount = dummycount - 1; + end else + case (mode) + mode_spi: begin + buffer = {buffer, io0}; + bitcount = bitcount + 1; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_dspi_rd, mode_dspi_wr: begin + buffer = {buffer, io1, io0}; + bitcount = bitcount + 2; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_qspi_rd, mode_qspi_wr: begin + buffer = {buffer, io3, io2, io1, io0}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_qspi_ddr_rd: begin + ddr_rd_edge; + end + mode_qspi_ddr_wr: begin + ddr_wr_edge; + end + endcase + end + end +endmodule diff --git a/picosoc/spiflash_tb.v b/picosoc/spiflash_tb.v new file mode 100644 index 0000000..a5b5edc --- /dev/null +++ b/picosoc/spiflash_tb.v @@ -0,0 +1,366 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg flash_csb = 1; + reg flash_clk = 0; + + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + reg flash_io0_oe = 0; + reg flash_io1_oe = 0; + reg flash_io2_oe = 0; + reg flash_io3_oe = 0; + + reg flash_io0_dout = 0; + reg flash_io1_dout = 0; + reg flash_io2_dout = 0; + reg flash_io3_dout = 0; + + assign flash_io0 = flash_io0_oe ? flash_io0_dout : 1'bz; + assign flash_io1 = flash_io1_oe ? flash_io1_dout : 1'bz; + assign flash_io2 = flash_io2_oe ? flash_io2_dout : 1'bz; + assign flash_io3 = flash_io3_oe ? flash_io3_dout : 1'bz; + + spiflash uut ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + localparam [23:0] offset = 24'h100000; + localparam [31:0] word0 = 32'h 00000093; + localparam [31:0] word1 = 32'h 00000193; + + reg [7:0] rdata; + integer errcount = 0; + + task expect; + input [7:0] data; + begin + if (data !== rdata) begin + $display("ERROR: Got %x (%b) but expected %x (%b).", rdata, rdata, data, data); + errcount = errcount + 1; + end + end + endtask + + task xfer_begin; + begin + #5; + flash_csb = 0; + $display("-- BEGIN"); + #5; + end + endtask + + task xfer_dummy; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + #5; + flash_clk = 0; + #5; + end + endtask + + task xfer_end; + begin + #5; + flash_csb = 1; + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + $display("-- END"); + $display(""); + #5; + end + endtask + + task xfer_spi; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + for (i = 0; i < 8; i=i+1) begin + flash_io0_dout = data[7-i]; + #5; + flash_clk = 1; + rdata[7-i] = flash_io1; + #5; + flash_clk = 0; + end + + $display("-- SPI SDR %02x %02x", data, rdata); + #5; + end + endtask + + task xfer_qspi_wr; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 1; + flash_io2_oe = 1; + flash_io3_oe = 1; + + flash_io0_dout = data[4]; + flash_io1_dout = data[5]; + flash_io2_dout = data[6]; + flash_io3_dout = data[7]; + + #5; + flash_clk = 1; + + #5; + flash_clk = 0; + flash_io0_dout = data[0]; + flash_io1_dout = data[1]; + flash_io2_dout = data[2]; + flash_io3_dout = data[3]; + + #5; + flash_clk = 1; + #5; + flash_clk = 0; + + $display("-- QSPI SDR %02x --", data); + #5; + end + endtask + + task xfer_qspi_rd; + integer i; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + rdata[4] = flash_io0; + rdata[5] = flash_io1; + rdata[6] = flash_io2; + rdata[7] = flash_io3; + + #5; + flash_clk = 0; + + #5; + flash_clk = 1; + rdata[0] = flash_io0; + rdata[1] = flash_io1; + rdata[2] = flash_io2; + rdata[3] = flash_io3; + + #5; + flash_clk = 0; + + $display("-- QSPI SDR -- %02x", rdata); + #5; + end + endtask + + task xfer_qspi_ddr_wr; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 1; + flash_io2_oe = 1; + flash_io3_oe = 1; + + flash_io0_dout = data[4]; + flash_io1_dout = data[5]; + flash_io2_dout = data[6]; + flash_io3_dout = data[7]; + + #5; + flash_clk = 1; + flash_io0_dout = data[0]; + flash_io1_dout = data[1]; + flash_io2_dout = data[2]; + flash_io3_dout = data[3]; + + #5; + flash_clk = 0; + + $display("-- QSPI DDR %02x --", data); + #5; + end + endtask + + task xfer_qspi_ddr_rd; + integer i; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + rdata[4] = flash_io0; + rdata[5] = flash_io1; + rdata[6] = flash_io2; + rdata[7] = flash_io3; + + #5; + flash_clk = 0; + rdata[0] = flash_io0; + rdata[1] = flash_io1; + rdata[2] = flash_io2; + rdata[3] = flash_io3; + + $display("-- QSPI DDR -- %02x", rdata); + #5; + end + endtask + + initial begin + $dumpfile("spiflash_tb.vcd"); + $dumpvars(0, testbench); + $display(""); + + $display("Reset (FFh)"); + xfer_begin; + xfer_spi(8'h ff); + xfer_end; + + $display("Power Up (ABh)"); + xfer_begin; + xfer_spi(8'h ab); + xfer_end; + + $display("Read Data (03h)"); + xfer_begin; + xfer_spi(8'h 03); + xfer_spi(offset[23:16]); + xfer_spi(offset[15:8]); + xfer_spi(offset[7:0]); + xfer_spi(8'h 00); expect(word0[7:0]); + xfer_spi(8'h 00); expect(word0[15:8]); + xfer_spi(8'h 00); expect(word0[23:16]); + xfer_spi(8'h 00); expect(word0[31:24]); + xfer_spi(8'h 00); expect(word1[7:0]); + xfer_spi(8'h 00); expect(word1[15:8]); + xfer_spi(8'h 00); expect(word1[23:16]); + xfer_spi(8'h 00); expect(word1[31:24]); + xfer_end; + + $display("Quad I/O Read (EBh)"); + xfer_begin; + xfer_spi(8'h eb); + xfer_qspi_wr(offset[23:16]); + xfer_qspi_wr(offset[15:8]); + xfer_qspi_wr(offset[7:0]); + xfer_qspi_wr(8'h a5); + repeat (8) xfer_dummy; + xfer_qspi_rd; expect(word0[7:0]); + xfer_qspi_rd; expect(word0[15:8]); + xfer_qspi_rd; expect(word0[23:16]); + xfer_qspi_rd; expect(word0[31:24]); + xfer_qspi_rd; expect(word1[7:0]); + xfer_qspi_rd; expect(word1[15:8]); + xfer_qspi_rd; expect(word1[23:16]); + xfer_qspi_rd; expect(word1[31:24]); + xfer_end; + + $display("Continous Quad I/O Read"); + xfer_begin; + xfer_qspi_wr(offset[23:16]); + xfer_qspi_wr(offset[15:8]); + xfer_qspi_wr(offset[7:0]); + xfer_qspi_wr(8'h ff); + repeat (8) xfer_dummy; + xfer_qspi_rd; expect(word0[7:0]); + xfer_qspi_rd; expect(word0[15:8]); + xfer_qspi_rd; expect(word0[23:16]); + xfer_qspi_rd; expect(word0[31:24]); + xfer_qspi_rd; expect(word1[7:0]); + xfer_qspi_rd; expect(word1[15:8]); + xfer_qspi_rd; expect(word1[23:16]); + xfer_qspi_rd; expect(word1[31:24]); + xfer_end; + + $display("DDR Quad I/O Read (EDh)"); + xfer_begin; + xfer_spi(8'h ed); + xfer_qspi_ddr_wr(offset[23:16]); + xfer_qspi_ddr_wr(offset[15:8]); + xfer_qspi_ddr_wr(offset[7:0]); + xfer_qspi_ddr_wr(8'h a5); + repeat (8) xfer_dummy; + xfer_qspi_ddr_rd; expect(word0[7:0]); + xfer_qspi_ddr_rd; expect(word0[15:8]); + xfer_qspi_ddr_rd; expect(word0[23:16]); + xfer_qspi_ddr_rd; expect(word0[31:24]); + xfer_qspi_ddr_rd; expect(word1[7:0]); + xfer_qspi_ddr_rd; expect(word1[15:8]); + xfer_qspi_ddr_rd; expect(word1[23:16]); + xfer_qspi_ddr_rd; expect(word1[31:24]); + xfer_end; + + $display("Continous DDR Quad I/O Read"); + xfer_begin; + xfer_qspi_ddr_wr(offset[23:16]); + xfer_qspi_ddr_wr(offset[15:8]); + xfer_qspi_ddr_wr(offset[7:0]); + xfer_qspi_ddr_wr(8'h ff); + repeat (8) xfer_dummy; + xfer_qspi_ddr_rd; expect(word0[7:0]); + xfer_qspi_ddr_rd; expect(word0[15:8]); + xfer_qspi_ddr_rd; expect(word0[23:16]); + xfer_qspi_ddr_rd; expect(word0[31:24]); + xfer_qspi_ddr_rd; expect(word1[7:0]); + xfer_qspi_ddr_rd; expect(word1[15:8]); + xfer_qspi_ddr_rd; expect(word1[23:16]); + xfer_qspi_ddr_rd; expect(word1[31:24]); + xfer_end; + + #5; + + if (errcount) begin + $display("FAIL"); + $stop; + end else begin + $display("PASS"); + end + end +endmodule diff --git a/picosoc/spimemio.v b/picosoc/spimemio.v new file mode 100644 index 0000000..b4ee446 --- /dev/null +++ b/picosoc/spimemio.v @@ -0,0 +1,579 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module spimemio ( + input clk, resetn, + + input valid, + output ready, + input [23:0] addr, + output reg [31:0] rdata, + + output flash_csb, + output flash_clk, + + output flash_io0_oe, + output flash_io1_oe, + output flash_io2_oe, + output flash_io3_oe, + + output flash_io0_do, + output flash_io1_do, + output flash_io2_do, + output flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di, + + input [3:0] cfgreg_we, + input [31:0] cfgreg_di, + output [31:0] cfgreg_do +); + reg xfer_resetn; + reg din_valid; + wire din_ready; + reg [7:0] din_data; + reg [3:0] din_tag; + reg din_cont; + reg din_qspi; + reg din_ddr; + reg din_rd; + + wire dout_valid; + wire [7:0] dout_data; + wire [3:0] dout_tag; + + reg [23:0] buffer; + + reg [23:0] rd_addr; + reg rd_valid; + reg rd_wait; + reg rd_inc; + + assign ready = valid && (addr == rd_addr) && rd_valid; + wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid; + + reg softreset; + + reg config_en; // cfgreg[31] + reg config_ddr; // cfgreg[22] + reg config_qspi; // cfgreg[21] + reg config_cont; // cfgreg[20] + reg [3:0] config_dummy; // cfgreg[19:16] + reg [3:0] config_oe; // cfgreg[11:8] + reg config_csb; // cfgreg[5] + reg config_clk; // cfgref[4] + reg [3:0] config_do; // cfgreg[3:0] + + assign cfgreg_do[31] = config_en; + assign cfgreg_do[30:23] = 0; + assign cfgreg_do[22] = config_ddr; + assign cfgreg_do[21] = config_qspi; + assign cfgreg_do[20] = config_cont; + assign cfgreg_do[19:16] = config_dummy; + assign cfgreg_do[15:12] = 0; + assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}; + assign cfgreg_do[7:6] = 0; + assign cfgreg_do[5] = flash_csb; + assign cfgreg_do[4] = flash_clk; + assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + + always @(posedge clk) begin + softreset <= !config_en || cfgreg_we; + if (!resetn) begin + softreset <= 1; + config_en <= 1; + config_csb <= 0; + config_clk <= 0; + config_oe <= 0; + config_do <= 0; + config_ddr <= 0; + config_qspi <= 0; + config_cont <= 0; + config_dummy <= 8; + end else begin + if (cfgreg_we[0]) begin + config_csb <= cfgreg_di[5]; + config_clk <= cfgreg_di[4]; + config_do <= cfgreg_di[3:0]; + end + if (cfgreg_we[1]) begin + config_oe <= cfgreg_di[11:8]; + end + if (cfgreg_we[2]) begin + config_ddr <= cfgreg_di[22]; + config_qspi <= cfgreg_di[21]; + config_cont <= cfgreg_di[20]; + config_dummy <= cfgreg_di[19:16]; + end + if (cfgreg_we[3]) begin + config_en <= cfgreg_di[31]; + end + end + end + + wire xfer_csb; + wire xfer_clk; + + wire xfer_io0_oe; + wire xfer_io1_oe; + wire xfer_io2_oe; + wire xfer_io3_oe; + + wire xfer_io0_do; + wire xfer_io1_do; + wire xfer_io2_do; + wire xfer_io3_do; + + reg xfer_io0_90; + reg xfer_io1_90; + reg xfer_io2_90; + reg xfer_io3_90; + + always @(negedge clk) begin + xfer_io0_90 <= xfer_io0_do; + xfer_io1_90 <= xfer_io1_do; + xfer_io2_90 <= xfer_io2_do; + xfer_io3_90 <= xfer_io3_do; + end + + assign flash_csb = config_en ? xfer_csb : config_csb; + assign flash_clk = config_en ? xfer_clk : config_clk; + + assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0]; + assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1]; + assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2]; + assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3]; + + assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0]; + assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1]; + assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2]; + assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3]; + + wire xfer_dspi = din_ddr && !din_qspi; + wire xfer_ddr = din_ddr && din_qspi; + + spimemio_xfer xfer ( + .clk (clk ), + .resetn (xfer_resetn ), + .din_valid (din_valid ), + .din_ready (din_ready ), + .din_data (din_data ), + .din_tag (din_tag ), + .din_cont (din_cont ), + .din_dspi (xfer_dspi ), + .din_qspi (din_qspi ), + .din_ddr (xfer_ddr ), + .din_rd (din_rd ), + .dout_valid (dout_valid ), + .dout_data (dout_data ), + .dout_tag (dout_tag ), + .flash_csb (xfer_csb ), + .flash_clk (xfer_clk ), + .flash_io0_oe (xfer_io0_oe ), + .flash_io1_oe (xfer_io1_oe ), + .flash_io2_oe (xfer_io2_oe ), + .flash_io3_oe (xfer_io3_oe ), + .flash_io0_do (xfer_io0_do ), + .flash_io1_do (xfer_io1_do ), + .flash_io2_do (xfer_io2_do ), + .flash_io3_do (xfer_io3_do ), + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di) + ); + + reg [3:0] state; + + always @(posedge clk) begin + xfer_resetn <= 1; + din_valid <= 0; + + if (!resetn || softreset) begin + state <= 0; + xfer_resetn <= 0; + rd_valid <= 0; + din_tag <= 0; + din_cont <= 0; + din_qspi <= 0; + din_ddr <= 0; + din_rd <= 0; + end else begin + if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data; + if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data; + if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data; + if (dout_valid && dout_tag == 4) begin + rdata <= {dout_data, buffer}; + rd_addr <= rd_inc ? rd_addr + 4 : addr; + rd_valid <= 1; + rd_wait <= rd_inc; + rd_inc <= 1; + end + + if (valid) + rd_wait <= 0; + + case (state) + 0: begin + din_valid <= 1; + din_data <= 8'h ff; + din_tag <= 0; + if (din_ready) begin + din_valid <= 0; + state <= 1; + end + end + 1: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 2; + end + end + 2: begin + din_valid <= 1; + din_data <= 8'h ab; + din_tag <= 0; + if (din_ready) begin + din_valid <= 0; + state <= 3; + end + end + 3: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 4; + end + end + 4: begin + rd_inc <= 0; + din_valid <= 1; + din_tag <= 0; + case ({config_ddr, config_qspi}) + 2'b11: din_data <= 8'h ED; + 2'b01: din_data <= 8'h EB; + 2'b10: din_data <= 8'h BB; + 2'b00: din_data <= 8'h 03; + endcase + if (din_ready) begin + din_valid <= 0; + state <= 5; + end + end + 5: begin + if (valid && !ready) begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[23:16]; + din_qspi <= config_qspi; + din_ddr <= config_ddr; + if (din_ready) begin + din_valid <= 0; + state <= 6; + end + end + end + 6: begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[15:8]; + if (din_ready) begin + din_valid <= 0; + state <= 7; + end + end + 7: begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[7:0]; + if (din_ready) begin + din_valid <= 0; + din_data <= 0; + state <= config_qspi || config_ddr ? 8 : 9; + end + end + 8: begin + din_valid <= 1; + din_tag <= 0; + din_data <= config_cont ? 8'h A5 : 8'h FF; + if (din_ready) begin + din_rd <= 1; + din_data <= config_dummy; + din_valid <= 0; + state <= 9; + end + end + 9: begin + din_valid <= 1; + din_tag <= 1; + if (din_ready) begin + din_valid <= 0; + state <= 10; + end + end + 10: begin + din_valid <= 1; + din_data <= 8'h 00; + din_tag <= 2; + if (din_ready) begin + din_valid <= 0; + state <= 11; + end + end + 11: begin + din_valid <= 1; + din_tag <= 3; + if (din_ready) begin + din_valid <= 0; + state <= 12; + end + end + 12: begin + if (!rd_wait || valid) begin + din_valid <= 1; + din_tag <= 4; + if (din_ready) begin + din_valid <= 0; + state <= 9; + end + end + end + endcase + + if (jump) begin + rd_inc <= 0; + rd_valid <= 0; + xfer_resetn <= 0; + if (config_cont) begin + state <= 5; + end else begin + state <= 4; + din_qspi <= 0; + din_ddr <= 0; + end + din_rd <= 0; + end + end + end +endmodule + +module spimemio_xfer ( + input clk, resetn, + + input din_valid, + output din_ready, + input [7:0] din_data, + input [3:0] din_tag, + input din_cont, + input din_dspi, + input din_qspi, + input din_ddr, + input din_rd, + + output dout_valid, + output [7:0] dout_data, + output [3:0] dout_tag, + + output reg flash_csb, + output reg flash_clk, + + output reg flash_io0_oe, + output reg flash_io1_oe, + output reg flash_io2_oe, + output reg flash_io3_oe, + + output reg flash_io0_do, + output reg flash_io1_do, + output reg flash_io2_do, + output reg flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di +); + reg [7:0] obuffer; + reg [7:0] ibuffer; + + reg [3:0] count; + reg [3:0] dummy_count; + + reg xfer_cont; + reg xfer_dspi; + reg xfer_qspi; + reg xfer_ddr; + reg xfer_ddr_q; + reg xfer_rd; + reg [3:0] xfer_tag; + reg [3:0] xfer_tag_q; + + reg [7:0] next_obuffer; + reg [7:0] next_ibuffer; + reg [3:0] next_count; + + reg fetch; + reg next_fetch; + reg last_fetch; + + always @(posedge clk) begin + xfer_ddr_q <= xfer_ddr; + xfer_tag_q <= xfer_tag; + end + + assign din_ready = din_valid && resetn && next_fetch; + + assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn; + assign dout_data = ibuffer; + assign dout_tag = xfer_tag_q; + + always @* begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + flash_io0_do = 0; + flash_io1_do = 0; + flash_io2_do = 0; + flash_io3_do = 0; + + next_obuffer = obuffer; + next_ibuffer = ibuffer; + next_count = count; + next_fetch = 0; + + if (dummy_count == 0) begin + casez ({xfer_ddr, xfer_qspi, xfer_dspi}) + 3'b 000: begin + flash_io0_oe = 1; + flash_io0_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[6:0], 1'b 0}; + next_count = count - |count; + end else begin + next_ibuffer = {ibuffer[6:0], flash_io1_di}; + end + + next_fetch = (next_count == 0); + end + 3'b 01?: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + flash_io2_oe = !xfer_rd; + flash_io3_oe = !xfer_rd; + + flash_io0_do = obuffer[4]; + flash_io1_do = obuffer[5]; + flash_io2_do = obuffer[6]; + flash_io3_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[3:0], 4'b 0000}; + next_count = count - {|count, 2'b00}; + end else begin + next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + end + + next_fetch = (next_count == 0); + end + 3'b 11?: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + flash_io2_oe = !xfer_rd; + flash_io3_oe = !xfer_rd; + + flash_io0_do = obuffer[4]; + flash_io1_do = obuffer[5]; + flash_io2_do = obuffer[6]; + flash_io3_do = obuffer[7]; + + next_obuffer = {obuffer[3:0], 4'b 0000}; + next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + next_count = count - {|count, 2'b00}; + + next_fetch = (next_count == 0); + end + 3'b ??1: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + + flash_io0_do = obuffer[6]; + flash_io1_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[5:0], 2'b 00}; + next_count = count - {|count, 1'b0}; + end else begin + next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di}; + end + + next_fetch = (next_count == 0); + end + endcase + end + end + + always @(posedge clk) begin + if (!resetn) begin + fetch <= 1; + last_fetch <= 1; + flash_csb <= 1; + flash_clk <= 0; + count <= 0; + dummy_count <= 0; + xfer_tag <= 0; + xfer_cont <= 0; + xfer_dspi <= 0; + xfer_qspi <= 0; + xfer_ddr <= 0; + xfer_rd <= 0; + end else begin + fetch <= next_fetch; + last_fetch <= xfer_ddr ? fetch : 1; + if (dummy_count) begin + flash_clk <= !flash_clk && !flash_csb; + dummy_count <= dummy_count - flash_clk; + end else + if (count) begin + flash_clk <= !flash_clk && !flash_csb; + obuffer <= next_obuffer; + ibuffer <= next_ibuffer; + count <= next_count; + end + if (din_valid && din_ready) begin + flash_csb <= 0; + flash_clk <= 0; + + count <= 8; + dummy_count <= din_rd ? din_data : 0; + obuffer <= din_data; + + xfer_tag <= din_tag; + xfer_cont <= din_cont; + xfer_dspi <= din_dspi; + xfer_qspi <= din_qspi; + xfer_ddr <= din_ddr; + xfer_rd <= din_rd; + end + end + end +endmodule diff --git a/picosoc/start.s b/picosoc/start.s new file mode 100644 index 0000000..e9e18db --- /dev/null +++ b/picosoc/start.s @@ -0,0 +1,159 @@ +.section .text + +start: + +# zero-initialize register file +addi x1, zero, 0 +# x2 (sp) is initialized by reset +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +# Update LEDs +li a0, 0x03000000 +li a1, 1 +sw a1, 0(a0) + +# zero initialize entire scratchpad memory +li a0, 0x00000000 +setmemloop: +sw a0, 0(a0) +addi a0, a0, 4 +blt a0, sp, setmemloop + +# Update LEDs +li a0, 0x03000000 +li a1, 3 +sw a1, 0(a0) + +# copy data section +la a0, _sidata +la a1, _sdata +la a2, _edata +bge a1, a2, end_init_data +loop_init_data: +lw a3, 0(a0) +sw a3, 0(a1) +addi a0, a0, 4 +addi a1, a1, 4 +blt a1, a2, loop_init_data +end_init_data: + +# Update LEDs +li a0, 0x03000000 +li a1, 7 +sw a1, 0(a0) + +# zero-init bss section +la a0, _sbss +la a1, _ebss +bge a0, a1, end_init_bss +loop_init_bss: +sw zero, 0(a0) +addi a0, a0, 4 +blt a0, a1, loop_init_bss +end_init_bss: + +# Update LEDs +li a0, 0x03000000 +li a1, 15 +sw a1, 0(a0) + +# call main +call main +loop: +j loop + +.global flashio_worker_begin +.global flashio_worker_end + +.balign 4 + +flashio_worker_begin: +# a0 ... data pointer +# a1 ... data length +# a2 ... optional WREN cmd (0 = disable) + +# address of SPI ctrl reg +li t0, 0x02000000 + +# Set CS high, IO0 is output +li t1, 0x120 +sh t1, 0(t0) + +# Enable Manual SPI Ctrl +sb zero, 3(t0) + +# Send optional WREN cmd +beqz a2, flashio_worker_L1 +li t5, 8 +andi t2, a2, 0xff +flashio_worker_L4: +srli t4, t2, 7 +sb t4, 0(t0) +ori t4, t4, 0x10 +sb t4, 0(t0) +slli t2, t2, 1 +andi t2, t2, 0xff +addi t5, t5, -1 +bnez t5, flashio_worker_L4 +sb t1, 0(t0) + +# SPI transfer +flashio_worker_L1: +beqz a1, flashio_worker_L3 +li t5, 8 +lbu t2, 0(a0) +flashio_worker_L2: +srli t4, t2, 7 +sb t4, 0(t0) +ori t4, t4, 0x10 +sb t4, 0(t0) +lbu t4, 0(t0) +andi t4, t4, 2 +srli t4, t4, 1 +slli t2, t2, 1 +or t2, t2, t4 +andi t2, t2, 0xff +addi t5, t5, -1 +bnez t5, flashio_worker_L2 +sb t2, 0(a0) +addi a0, a0, 1 +addi a1, a1, -1 +j flashio_worker_L1 +flashio_worker_L3: + +# Back to MEMIO mode +li t1, 0x80 +sb t1, 3(t0) + +ret + +.balign 4 +flashio_worker_end: -- cgit