diff --git a/spiflash/.gitignore b/spiflash/.gitignore new file mode 100644 index 0000000..76f21cb --- /dev/null +++ b/spiflash/.gitignore @@ -0,0 +1,11 @@ +/testbench.vcd +/testbench.vvp +/firmware.elf +/firmware_vma.elf +/firmware.hex +/firmware.bin +/design.asc +/design.bin +/design.blif +/design.log +/design.rpt diff --git a/spiflash/Makefile b/spiflash/Makefile new file mode 100644 index 0000000..59faf6e --- /dev/null +++ b/spiflash/Makefile @@ -0,0 +1,40 @@ + +test: testbench.vvp firmware.hex + vvp -N $< + +prog: design.bin firmware.bin + iceprog design.bin + iceprog -o 1M firmware.bin + +testbench.vvp: spiflash.v spimemio.v testbench.v top.v ../picorv32.v + iverilog -s testbench -o $@ $^ + +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 top.v ../picorv32.v + yosys -ql design.log -p 'synth_ice40 -top top -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 + rm -f firmware.elf firmware_vma.elf firmware.hex firmware.bin + rm -f design.blif design.log design.asc design.rpt design.bin + +.PHONY: test prog clean + diff --git a/spiflash/README b/spiflash/README new file mode 100644 index 0000000..753458b --- /dev/null +++ b/spiflash/README @@ -0,0 +1,18 @@ + +PicoRV32 SPI-Flash Demo +======================= + +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. + diff --git a/spiflash/firmware.s b/spiflash/firmware.s new file mode 100644 index 0000000..338ae5a --- /dev/null +++ b/spiflash/firmware.s @@ -0,0 +1,8 @@ +start: + li x1,0xc0000000 + sw x0,0(x0) +loop: lw x2,0(x0) + addi x2,x2,1 + sw x2,0(x1) + sw x2,0(x0) + j loop diff --git a/spiflash/pinout.pcf b/spiflash/pinout.pcf new file mode 100644 index 0000000..078b239 --- /dev/null +++ b/spiflash/pinout.pcf @@ -0,0 +1,81 @@ + +# Pinout for the iCE40-HX8K Breakout Board + +set_io clk J3 + +set_io spi_cs R12 +set_io spi_sclk R11 +set_io spi_miso P11 +set_io spi_mosi P12 + +# 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/spiflash/spiflash.v b/spiflash/spiflash.v new file mode 100644 index 0000000..933f3a2 --- /dev/null +++ b/spiflash/spiflash.v @@ -0,0 +1,105 @@ +module spiflash ( + input spi_cs, + output reg spi_miso, + input spi_mosi, + input spi_sclk +); + localparam verbose = 1; + + reg [7:0] buffer; + integer bitcount = 0; + integer bytecount = 0; + + reg [7:0] spi_cmd; + reg [23:0] spi_addr; + + reg [7:0] spi_in; + reg [7:0] spi_out; + reg spi_io_vld; + + reg powered_up = 0; + reg in_xfer = 0; + + // 16 MB (128Mb) Flash + reg [7:0] memory [0:16*1024*1024-1]; + + initial begin + $readmemh("firmware.hex", memory); + end + + task spi_action; + begin + if (verbose) begin + if (bytecount == 1) + $write(""); + $write("= 4) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (verbose) begin + $write(":%02x>", buffer); + spi_out = buffer; + spi_io_vld = 1; + $fflush; + end + end + endtask + + always @(spi_cs) begin + if (spi_cs) begin + if (verbose && in_xfer) begin + $display(""); + $fflush; + end + buffer = 0; + in_xfer = 0; + bitcount = 0; + bytecount = 0; + spi_miso = 0; + end + end + + always @(spi_cs, spi_sclk) begin + spi_io_vld = 0; + if (!spi_cs && !spi_sclk) begin + spi_miso = buffer[7]; + end + end + + always @(posedge spi_sclk) begin + if (!spi_cs) begin + buffer = {buffer, spi_mosi}; + bitcount = bitcount + 1; + if (bitcount == 8) begin + in_xfer = 1; + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + end +endmodule diff --git a/spiflash/spimemio.v b/spiflash/spimemio.v new file mode 100644 index 0000000..a87055e --- /dev/null +++ b/spiflash/spimemio.v @@ -0,0 +1,65 @@ +module spimemio ( + input clk, resetn, + + input valid, + output reg ready, + input [23:0] addr, + output reg [31:0] rdata, + + output reg spi_cs, + output reg spi_sclk, + output reg spi_mosi, + input spi_miso +); + reg [23:0] addr_q; + reg addr_q_vld; + + reg [31:0] buffer; + reg [6:0] xfer_cnt; + reg xfer_wait; + + always @(posedge clk) begin + ready <= 0; + if (!resetn) begin + spi_cs <= 1; + spi_sclk <= 1; + xfer_cnt <= 8; + buffer <= 8'hAB << 24; + addr_q_vld <= 0; + xfer_wait <= 0; + end else + if (xfer_cnt) begin + if (spi_cs) begin + spi_cs <= 0; + end else + if (spi_sclk) begin + spi_sclk <= 0; + spi_mosi <= buffer[31]; + end else begin + spi_sclk <= 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; + xfer_cnt <= 32; + xfer_wait <= 1; + end else begin + spi_cs <= 1; + buffer <= {8'h 03, addr}; + addr_q <= addr + 4; + addr_q_vld <= 1; + xfer_cnt <= 64; + xfer_wait <= 1; + end + end + end +endmodule diff --git a/spiflash/testbench.v b/spiflash/testbench.v new file mode 100644 index 0000000..6b4aaef --- /dev/null +++ b/spiflash/testbench.v @@ -0,0 +1,37 @@ +module testbench; + reg clk = 1; + always #5 clk = ~clk; + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + repeat (10000) @(posedge clk); + $display(""); + $finish; + end + + wire [31:0] gpio_i = 0; + wire [31:0] gpio_o; + + wire spi_cs; + wire spi_sclk; + wire spi_mosi; + wire spi_miso; + + top uut ( + .clk (clk ), + .gpio_i (gpio_i ), + .gpio_o (gpio_o ), + .spi_cs (spi_cs ), + .spi_sclk(spi_sclk), + .spi_mosi(spi_mosi), + .spi_miso(spi_miso) + ); + + spiflash spiflash ( + .spi_cs (spi_cs ), + .spi_sclk(spi_sclk), + .spi_mosi(spi_mosi), + .spi_miso(spi_miso) + ); +endmodule diff --git a/spiflash/top.v b/spiflash/top.v new file mode 100644 index 0000000..aba6cb5 --- /dev/null +++ b/spiflash/top.v @@ -0,0 +1,89 @@ +module top ( + input clk, + output trap, + + input [31:0] gpio_i, + output reg [31:0] gpio_o, + + output spi_cs, + output spi_sclk, + output spi_mosi, + input spi_miso +); + 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), + + .spi_cs (spi_cs ), + .spi_sclk (spi_sclk), + .spi_mosi (spi_mosi), + .spi_miso (spi_miso) + ); + + 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