aboutsummaryrefslogtreecommitdiffstats
path: root/picosoc
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2017-08-07 13:38:07 +0200
committerClifford Wolf <clifford@clifford.at>2017-08-07 13:38:07 +0200
commitcaef4e3753d2c76de5d7030fcc2e3a1c331c64c6 (patch)
tree57162c7dd5d90c5b18e0ecd3380d9fd67f6771ff /picosoc
parent571f5d5df742b3168d1e8c26e8ef0b8247960666 (diff)
downloadpicorv32-caef4e3753d2c76de5d7030fcc2e3a1c331c64c6.tar.gz
picorv32-caef4e3753d2c76de5d7030fcc2e3a1c331c64c6.zip
Rename "spiflash" example to "picosoc"
Diffstat (limited to 'picosoc')
-rw-r--r--picosoc/.gitignore13
-rw-r--r--picosoc/Makefile46
-rw-r--r--picosoc/README.md27
-rw-r--r--picosoc/firmware.s56
-rw-r--r--picosoc/picosoc.v111
-rw-r--r--picosoc/pinout.pcf83
-rw-r--r--picosoc/spiflash.v344
-rw-r--r--picosoc/spiflash_tb.v363
-rw-r--r--picosoc/spimemio.v109
-rw-r--r--picosoc/testbench.v74
10 files changed, 1226 insertions, 0 deletions
diff --git a/picosoc/.gitignore b/picosoc/.gitignore
new file mode 100644
index 0000000..e88e84f
--- /dev/null
+++ b/picosoc/.gitignore
@@ -0,0 +1,13 @@
+/testbench.vcd
+/testbench.vvp
+/spiflash_tb.vcd
+/spiflash_tb.vvp
+/firmware.elf
+/firmware_vma.elf
+/firmware.hex
+/firmware.bin
+/design.asc
+/design.bin
+/design.blif
+/design.log
+/design.rpt
diff --git a/picosoc/Makefile b/picosoc/Makefile
new file mode 100644
index 0000000..1436321
--- /dev/null
+++ b/picosoc/Makefile
@@ -0,0 +1,46 @@
+
+testbench: testbench.vvp firmware.hex
+ vvp -N $<
+
+testbench.vvp: spiflash.v spimemio.v testbench.v picosoc.v ../picorv32.v
+ iverilog -s testbench -o $@ $^
+
+spiflash_tb: spiflash_tb.vvp firmware.hex
+ vvp -N $<
+
+spiflash_tb.vvp: spiflash.v spiflash_tb.v
+ iverilog -s testbench -o $@ $^
+
+prog: design.bin firmware.bin
+ iceprog design.bin
+ iceprog -o 1M firmware.bin
+
+firmware.elf: firmware.s
+ riscv32-unknown-elf-gcc -c -o firmware.elf firmware.s
+
+firmware_vma.elf: firmware.elf
+ riscv32-unknown-elf-objcopy --change-section-vma .text=0x00100000 firmware.elf firmware_vma.elf
+
+firmware.hex: firmware_vma.elf
+ riscv32-unknown-elf-objcopy -O verilog firmware_vma.elf firmware.hex
+
+firmware.bin: firmware.elf
+ riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin
+
+design.blif: spimemio.v picosoc.v ../picorv32.v
+ yosys -ql design.log -p 'synth_ice40 -top picosoc -blif design.blif' $^
+
+design.asc: pinout.pcf design.blif
+ arachne-pnr -d 8k -o design.asc -p pinout.pcf design.blif
+
+design.bin: design.asc
+ icetime -d hx8k -c 12 -mtr design.rpt design.asc
+ icepack design.asc design.bin
+
+clean:
+ rm -f testbench.vvp testbench.vcd spiflash_tb.vvp spiflash_tb.vcd
+ rm -f firmware.elf firmware_vma.elf firmware.hex firmware.bin
+ rm -f design.blif design.log design.asc design.rpt design.bin
+
+.PHONY: testbench spiflash_tb prog clean
+
diff --git a/picosoc/README.md b/picosoc/README.md
new file mode 100644
index 0000000..bf3bff6
--- /dev/null
+++ b/picosoc/README.md
@@ -0,0 +1,27 @@
+
+PicoSoC - A simple example SoC using PicoRV32
+=============================================
+
+This is a simple PicoRV32 example design that can run code directly from an SPI
+flash chip. This example design uses the Lattice iCE40-HX8K Breakout Board.
+
+The flash is mapped to the memory region starting at 0x80000000. The reset
+vector is set to 0x80100000, i.e. at the 1MB offset inside the flash memory.
+
+A small scratchpad memory (default 256 words, i.e. 1 kB) is mapped to address
+0x00000000. A simple GPIO controller is mapped to address 0xC0000000.
+
+Run `make test` to run the test bench (and create `testbench.vcd`).
+
+Run `make prog` to build the configuration bit-stream and firmware images
+and upload them to a connected iCE40-HX8K Breakout Board.
+
+| File | Description |
+| --------------------------- | --------------------------------------------------------------- |
+| [picosoc.v](picosoc.v) | Top-level Verilog module for the design |
+| [spimemio.v](spimemio.v) | Memory controller that interfaces to external SPI flash |
+| [spiflash.v](spiflash.v) | Simulation model of an SPI flash (used by testbench.v) |
+| [testbench.v](testbench.v) | Simple test bench for the design (requires firmware.hex). |
+| [firmware.s](firmware.s) | Assembler source for firmware.hex/firmware.bin. |
+| [pinout.pcf](pinout.pcf) | Pin constraints for implementation on iCE40-HX8K Breakout Board |
+
diff --git a/picosoc/firmware.s b/picosoc/firmware.s
new file mode 100644
index 0000000..9b12f0c
--- /dev/null
+++ b/picosoc/firmware.s
@@ -0,0 +1,56 @@
+# write RAM code (a sequence of nops followed by ret)
+li x5,0x00000013 # nop
+sw x5,4(x0)
+sw x5,8(x0)
+sw x5,12(x0)
+sw x5,16(x0)
+sw x5,20(x0)
+sw x5,24(x0)
+sw x5,28(x0)
+sw x5,32(x0)
+sw x5,36(x0)
+sw x5,40(x0)
+sw x5,44(x0)
+sw x5,48(x0)
+sw x5,52(x0)
+sw x5,56(x0)
+sw x5,60(x0)
+sw x5,64(x0)
+sw x5,68(x0)
+sw x5,72(x0)
+sw x5,76(x0)
+li x5,0x00008067 # ret
+sw x5,80(x0)
+
+# setup gpio address in x5
+li x5,0xc0000000
+sw x0,0(x5)
+
+# initial entry point into RAM code
+li x3,4
+
+# initialize RAM counter
+sw x0,0(x0)
+
+# start of loop. remember this address
+auipc x4,0
+
+# execute RAM code, come back here
+jalr x3
+
+# load counter and increment
+lw x6,0(x0)
+addi x6,x6,1
+
+# store counter and update gpios
+sw x6,0(x5)
+sw x6,0(x0)
+
+# calculate new entry point into RAM code
+slli x3,x6,2
+andi x3,x3,63
+addi x3,x3,4
+
+# execute RAM code, come back to start of loop
+mv x1,x4
+jr x3
diff --git a/picosoc/picosoc.v b/picosoc/picosoc.v
new file mode 100644
index 0000000..d1b01dd
--- /dev/null
+++ b/picosoc/picosoc.v
@@ -0,0 +1,111 @@
+/*
+ * PicoSoC - A simple example SoC using PicoRV32
+ *
+ * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 picosoc (
+ input clk,
+ output trap,
+
+ input [31:0] gpio_i,
+ output reg [31:0] gpio_o,
+
+ output flash_csb,
+ output flash_clk,
+ output flash_io0,
+ input flash_io1,
+ input flash_io2,
+ input flash_io3
+);
+ parameter integer MEM_WORDS = 256;
+ parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
+ parameter [31:0] PROGADDR_RESET = 32'h 8010_0000; // 1 MB into flash
+
+ reg [5:0] reset_cnt = 0;
+ wire resetn = &reset_cnt;
+
+ always @(posedge clk) begin
+ reset_cnt <= reset_cnt + !resetn;
+ end
+
+ wire mem_valid;
+ wire mem_instr;
+ reg mem_ready;
+ wire [31:0] mem_addr;
+ wire [31:0] mem_wdata;
+ wire [3:0] mem_wstrb;
+ reg [31:0] mem_rdata;
+
+ wire spimem_ready;
+ wire [31:0] spimem_rdata;
+
+ picorv32 #(
+ .STACKADDR(STACKADDR),
+ .PROGADDR_RESET(PROGADDR_RESET)
+ ) cpu (
+ .clk (clk ),
+ .resetn (resetn ),
+ .trap (trap ),
+ .mem_valid (mem_valid ),
+ .mem_instr (mem_instr ),
+ .mem_ready (mem_ready || spimem_ready),
+ .mem_addr (mem_addr ),
+ .mem_wdata (mem_wdata ),
+ .mem_wstrb (mem_wstrb ),
+ .mem_rdata (spimem_ready ? spimem_rdata : mem_rdata )
+ );
+
+ spimemio spimemio (
+ .clk(clk),
+ .resetn(resetn),
+ .valid (mem_valid && mem_addr[31:30] == 2'b10),
+ .ready (spimem_ready),
+ .addr (mem_addr[23:0]),
+ .rdata (spimem_rdata),
+
+ .flash_csb (flash_csb),
+ .flash_clk (flash_clk),
+ .flash_io0 (flash_io0),
+ .flash_io1 (flash_io1),
+ .flash_io2 (flash_io2),
+ .flash_io3 (flash_io3)
+ );
+
+ reg [31:0] memory [0:MEM_WORDS-1];
+
+ always @(posedge clk) begin
+ mem_ready <= 0;
+ if (mem_valid && !mem_ready) begin
+ if (mem_addr < 4*MEM_WORDS) begin
+ mem_ready <= 1;
+ mem_rdata <= memory[mem_addr >> 2];
+ if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0];
+ if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8];
+ if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16];
+ if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24];
+ end
+ if (mem_addr == 32'h c000_0000) begin
+ mem_ready <= 1;
+ mem_rdata <= gpio_i;
+ if (mem_wstrb[0]) gpio_o[ 7: 0] <= mem_wdata[ 7: 0];
+ if (mem_wstrb[1]) gpio_o[15: 8] <= mem_wdata[15: 8];
+ if (mem_wstrb[2]) gpio_o[23:16] <= mem_wdata[23:16];
+ if (mem_wstrb[3]) gpio_o[31:24] <= mem_wdata[31:24];
+ end
+ end
+ end
+endmodule
diff --git a/picosoc/pinout.pcf b/picosoc/pinout.pcf
new file mode 100644
index 0000000..dea0376
--- /dev/null
+++ b/picosoc/pinout.pcf
@@ -0,0 +1,83 @@
+
+# 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
+set_io flash_io2 T9 # center on J3
+set_io flash_io3 P8 # center on J3
+
+# All the GPIO pins below are connected to pins that are floating
+# on the iCE40-HX8K Breakout Board, expect the marked LED pins.
+
+set_io trap C3 # LED7
+
+set_io gpio_o[0] C14
+set_io gpio_o[1] D13
+set_io gpio_o[2] C12
+set_io gpio_o[3] E11
+set_io gpio_o[4] C13
+set_io gpio_o[5] E10
+set_io gpio_o[6] C11
+set_io gpio_o[7] D11
+set_io gpio_o[8] C10
+set_io gpio_o[9] D10
+set_io gpio_o[10] L10
+set_io gpio_o[11] E9
+set_io gpio_o[12] B5 # LED0
+set_io gpio_o[13] B4 # LED1
+set_io gpio_o[14] A2 # LED2
+set_io gpio_o[15] A1 # LED3
+set_io gpio_o[16] C5 # LED4
+set_io gpio_o[17] C4 # LED5
+set_io gpio_o[18] B3 # LED6
+set_io gpio_o[19] D9
+set_io gpio_o[20] F9
+set_io gpio_o[21] D8
+set_io gpio_o[22] D7
+set_io gpio_o[23] D6
+set_io gpio_o[24] E6
+set_io gpio_o[25] D5
+set_io gpio_o[26] D4
+set_io gpio_o[27] E5
+set_io gpio_o[28] D3
+set_io gpio_o[29] M13
+set_io gpio_o[30] M14
+set_io gpio_o[31] L12
+
+set_io gpio_i[0] L13
+set_io gpio_i[1] L14
+set_io gpio_i[2] K12
+set_io gpio_i[3] J10
+set_io gpio_i[4] J11
+set_io gpio_i[5] K13
+set_io gpio_i[6] J12
+set_io gpio_i[7] J13
+set_io gpio_i[8] J16
+set_io gpio_i[9] H13
+set_io gpio_i[10] H12
+set_io gpio_i[11] G10
+set_io gpio_i[12] G11
+set_io gpio_i[13] G13
+set_io gpio_i[14] G12
+set_io gpio_i[15] F12
+set_io gpio_i[16] F11
+set_io gpio_i[17] E14
+set_io gpio_i[18] F13
+set_io gpio_i[19] E13
+set_io gpio_i[20] N6
+set_io gpio_i[21] P4
+set_io gpio_i[22] N5
+set_io gpio_i[23] P5
+set_io gpio_i[24] M7
+set_io gpio_i[25] N7
+set_io gpio_i[26] P6
+set_io gpio_i[27] M8
+set_io gpio_i[28] L9
+set_io gpio_i[29] P7
+set_io gpio_i[30] N9
+set_io gpio_i[31] M9
+
diff --git a/picosoc/spiflash.v b/picosoc/spiflash.v
new file mode 100644
index 0000000..c1e9615
--- /dev/null
+++ b/picosoc/spiflash.v
@@ -0,0 +1,344 @@
+/*
+ * PicoSoC - A simple example SoC using PicoRV32
+ *
+ * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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.
+ *
+ *
+ * Supported commands:
+ * AB, B9, FF, 03, EB, ED
+ *
+ * Well written SPI flash data sheets:
+ * Cypress S25FL064L http://www.cypress.com/file/316661/download
+ *
+ */
+
+module spiflash (
+ input csb,
+ input clk,
+ inout io0, // MOSI
+ inout io1, // MISO
+ inout io2,
+ inout io3
+);
+ localparam verbose = 0;
+
+ 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 qspi_active = 0;
+ reg powered_up = 0;
+
+ localparam [3:0] mode_spi = 1;
+ localparam [3:0] mode_qspi_rd = 2;
+ localparam [3:0] mode_qspi_wr = 3;
+ localparam [3:0] mode_qspi_ddr_rd = 4;
+ localparam [3:0] mode_qspi_ddr_wr = 5;
+
+ 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 io0 = io0_oe ? io0_dout : 1'bz;
+ assign io1 = io1_oe ? io1_dout : 1'bz;
+ assign io2 = io2_oe ? io2_dout : 1'bz;
+ assign 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];
+
+ initial begin
+ $readmemh("firmware.hex", 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)
+ qspi_active = 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 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 = 1;
+ 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 = 1;
+ 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("<SPI-START>");
+ $write("<SPI:%02x:%02x>", 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_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_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..769a3a0
--- /dev/null
+++ b/picosoc/spiflash_tb.v
@@ -0,0 +1,363 @@
+/*
+ * PicoSoC - A simple example SoC using PicoRV32
+ *
+ * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 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 01300293;
+ localparam [31:0] word1 = 32'h 00502223;
+
+ 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
+ #50;
+ flash_csb = 0;
+ $display("-- BEGIN");
+ #50;
+ end
+ endtask
+
+ task xfer_dummy;
+ begin
+ flash_io0_oe = 0;
+ flash_io1_oe = 0;
+ flash_io2_oe = 0;
+ flash_io3_oe = 0;
+
+ #50;
+ flash_clk = 1;
+ #50;
+ flash_clk = 0;
+ #50;
+ end
+ endtask
+
+ task xfer_end;
+ begin
+ #50;
+ flash_csb = 1;
+ flash_io0_oe = 0;
+ flash_io1_oe = 0;
+ flash_io2_oe = 0;
+ flash_io3_oe = 0;
+ $display("-- END");
+ $display("");
+ #50;
+ 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];
+ #50;
+ rdata[7-i] = flash_io1;
+ flash_clk <= 1;
+ #50;
+ flash_clk = 0;
+ end
+
+ $display("-- SPI SDR %02x %02x", data, rdata);
+ #50;
+ 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];
+
+ #50;
+ flash_clk = 1;
+ #50;
+ flash_clk = 0;
+
+ flash_io0_dout = data[0];
+ flash_io1_dout = data[1];
+ flash_io2_dout = data[2];
+ flash_io3_dout = data[3];
+
+ #50;
+ flash_clk = 1;
+ #50;
+ flash_clk = 0;
+
+ $display("-- QSPI SDR %02x --", data);
+ #50;
+ 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;
+
+ #50;
+ rdata[4] = flash_io0;
+ rdata[5] = flash_io1;
+ rdata[6] = flash_io2;
+ rdata[7] = flash_io3;
+ flash_clk <= 1;
+ #50;
+ flash_clk = 0;
+
+ #50;
+ rdata[0] = flash_io0;
+ rdata[1] = flash_io1;
+ rdata[2] = flash_io2;
+ rdata[3] = flash_io3;
+ flash_clk <= 1;
+ #50;
+ flash_clk = 0;
+
+ $display("-- QSPI SDR -- %02x", rdata);
+ #50;
+ 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];
+
+ #50;
+ flash_clk = 1;
+
+ flash_io0_dout <= data[0];
+ flash_io1_dout <= data[1];
+ flash_io2_dout <= data[2];
+ flash_io3_dout <= data[3];
+
+ #50;
+ flash_clk = 0;
+
+ $display("-- QSPI DDR %02x --", data);
+ #50;
+ 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;
+
+ #50;
+ rdata[4] = flash_io0;
+ rdata[5] = flash_io1;
+ rdata[6] = flash_io2;
+ rdata[7] = flash_io3;
+ flash_clk <= 1;
+
+ #50;
+ rdata[0] = flash_io0;
+ rdata[1] = flash_io1;
+ rdata[2] = flash_io2;
+ rdata[3] = flash_io3;
+ flash_clk <= 0;
+
+ $display("-- QSPI DDR -- %02x", rdata);
+ #50;
+ 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);
+ 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);
+ 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);
+ 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);
+ 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;
+
+ #500;
+
+ 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..6bb4ea6
--- /dev/null
+++ b/picosoc/spimemio.v
@@ -0,0 +1,109 @@
+/*
+ * PicoSoC - A simple example SoC using PicoRV32
+ *
+ * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 reg ready,
+ input [23:0] addr,
+ output reg [31:0] rdata,
+
+ output reg flash_csb,
+ output reg flash_clk,
+ output flash_io0,
+ input flash_io1,
+ input flash_io2,
+ input flash_io3
+);
+ parameter ENABLE_PREFETCH = 1;
+
+ reg [23:0] addr_q;
+ reg addr_q_vld;
+
+ reg [31:0] buffer;
+ reg [6:0] xfer_cnt;
+ reg xfer_wait;
+ reg prefetch;
+
+ reg spi_mosi;
+ wire spi_miso;
+
+ assign flash_io0 = spi_mosi;
+ assign spi_miso = flash_io1;
+
+ always @(posedge clk) begin
+ ready <= 0;
+ if (!resetn) begin
+ flash_csb <= 1;
+ flash_clk <= 1;
+ xfer_cnt <= 8;
+ buffer <= 8'hAB << 24;
+ addr_q_vld <= 0;
+ xfer_wait <= 0;
+ prefetch <= 0;
+ end else
+ if (xfer_cnt) begin
+ if (flash_csb) begin
+ flash_csb <= 0;
+ end else
+ if (flash_clk) begin
+ flash_clk <= 0;
+ spi_mosi <= buffer[31];
+ end else begin
+ flash_clk <= 1;
+ buffer <= {buffer, spi_miso};
+ xfer_cnt <= xfer_cnt - 1;
+ end
+ end else
+ if (xfer_wait) begin
+ ready <= 1;
+ rdata <= {buffer[7:0], buffer[15:8], buffer[23:16], buffer[31:24]};
+ xfer_wait <= 0;
+ end else
+ if (valid && !ready) begin
+ if (addr_q_vld && addr_q == addr) begin
+ addr_q <= addr + 4;
+ addr_q_vld <= 1;
+ if (!prefetch)
+ xfer_cnt <= 32;
+ xfer_wait <= 1;
+ prefetch <= 0;
+ end else begin
+ flash_csb <= 1;
+ buffer <= {8'h 03, addr};
+ addr_q <= addr + 4;
+ addr_q_vld <= 1;
+ xfer_cnt <= 64;
+ xfer_wait <= 1;
+ prefetch <= 0;
+ end
+ end else if (ENABLE_PREFETCH && !prefetch) begin
+ prefetch <= 1;
+ xfer_cnt <= 32;
+ end
+
+ if (ENABLE_PREFETCH && resetn && prefetch && valid && !ready && addr_q != addr) begin
+ prefetch <= 0;
+ xfer_cnt <= 0;
+ xfer_wait <= 0;
+ flash_clk <= 1;
+ end
+ end
+endmodule
diff --git a/picosoc/testbench.v b/picosoc/testbench.v
new file mode 100644
index 0000000..47cef3e
--- /dev/null
+++ b/picosoc/testbench.v
@@ -0,0 +1,74 @@
+/*
+ * PicoSoC - A simple example SoC using PicoRV32
+ *
+ * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 testbench;
+ reg clk = 1;
+ always #5 clk = ~clk;
+
+ initial begin
+ $dumpfile("testbench.vcd");
+ $dumpvars(0, testbench);
+ repeat (100000) @(posedge clk);
+ $display("");
+ $display("[TIMEOUT]");
+ $stop;
+ end
+
+ wire [31:0] gpio_i = 0;
+ wire [31:0] gpio_o;
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+ wire flash_io2;
+ wire flash_io3;
+
+ always @(gpio_o) begin
+ $write("<GPIO:%02x>", gpio_o[7:0]);
+ if (gpio_o == 63) begin
+ $display("[OK]");
+ $finish;
+ end
+ if (gpio_o % 8 == 7) begin
+ $display("");
+ end
+ end
+
+ picosoc uut (
+ .clk (clk ),
+ .gpio_i (gpio_i ),
+ .gpio_o (gpio_o ),
+ .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)
+ );
+endmodule