diff options
author | Clifford Wolf <clifford@clifford.at> | 2017-08-07 13:38:07 +0200 |
---|---|---|
committer | Clifford Wolf <clifford@clifford.at> | 2017-08-07 13:38:07 +0200 |
commit | caef4e3753d2c76de5d7030fcc2e3a1c331c64c6 (patch) | |
tree | 57162c7dd5d90c5b18e0ecd3380d9fd67f6771ff /picosoc | |
parent | 571f5d5df742b3168d1e8c26e8ef0b8247960666 (diff) | |
download | picorv32-caef4e3753d2c76de5d7030fcc2e3a1c331c64c6.tar.gz picorv32-caef4e3753d2c76de5d7030fcc2e3a1c331c64c6.zip |
Rename "spiflash" example to "picosoc"
Diffstat (limited to 'picosoc')
-rw-r--r-- | picosoc/.gitignore | 13 | ||||
-rw-r--r-- | picosoc/Makefile | 46 | ||||
-rw-r--r-- | picosoc/README.md | 27 | ||||
-rw-r--r-- | picosoc/firmware.s | 56 | ||||
-rw-r--r-- | picosoc/picosoc.v | 111 | ||||
-rw-r--r-- | picosoc/pinout.pcf | 83 | ||||
-rw-r--r-- | picosoc/spiflash.v | 344 | ||||
-rw-r--r-- | picosoc/spiflash_tb.v | 363 | ||||
-rw-r--r-- | picosoc/spimemio.v | 109 | ||||
-rw-r--r-- | picosoc/testbench.v | 74 |
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 |