diff --git a/spiflash/.gitignore b/spiflash/.gitignore index 76f21cb..e88e84f 100644 --- a/spiflash/.gitignore +++ b/spiflash/.gitignore @@ -1,5 +1,7 @@ /testbench.vcd /testbench.vvp +/spiflash_tb.vcd +/spiflash_tb.vvp /firmware.elf /firmware_vma.elf /firmware.hex diff --git a/spiflash/Makefile b/spiflash/Makefile index 59faf6e..7bb244c 100644 --- a/spiflash/Makefile +++ b/spiflash/Makefile @@ -1,14 +1,20 @@ -test: testbench.vvp firmware.hex +testbench: testbench.vvp firmware.hex vvp -N $< +testbench.vvp: spiflash.v spimemio.v testbench.v top.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 -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 @@ -32,9 +38,9 @@ design.bin: design.asc icepack design.asc design.bin clean: - rm -f testbench.vvp testbench.vcd + 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: test prog clean +.PHONY: testbench spiflash_tb prog clean diff --git a/spiflash/spiflash.v b/spiflash/spiflash.v index e390dff..c42bd3b 100644 --- a/spiflash/spiflash.v +++ b/spiflash/spiflash.v @@ -15,6 +15,13 @@ * 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 ( @@ -30,8 +37,10 @@ module spiflash ( 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; @@ -40,12 +49,40 @@ module spiflash ( reg qspi_active = 0; reg powered_up = 0; - reg in_xfer = 0; - reg spi_miso; + 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; - wire spi_mosi = io0; - assign io1 = spi_miso; + 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]; @@ -60,15 +97,18 @@ module spiflash ( if (bytecount == 1) begin spi_cmd = buffer; - if (spi_cmd == 8'hAB) + + if (spi_cmd == 8'h ab) powered_up = 1; - if (spi_cmd == 8'hB9) + + if (spi_cmd == 8'h b9) powered_up = 0; - if (spi_cmd == 8'hFF) + + if (spi_cmd == 8'h ff) qspi_active = 0; end - if (powered_up && spi_cmd == 'h03) begin + if (powered_up && spi_cmd == 'h 03) begin if (bytecount == 2) spi_addr[23:16] = buffer; @@ -84,6 +124,56 @@ module spiflash ( 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; @@ -96,37 +186,159 @@ module spiflash ( 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 && in_xfer) begin + if (verbose) begin $display(""); $fflush; end buffer = 0; - in_xfer = 0; bitcount = 0; bytecount = 0; - spi_miso = 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 - spi_miso = buffer[7]; + 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 - buffer = {buffer, spi_mosi}; - bitcount = bitcount + 1; - if (bitcount == 8) begin - in_xfer = 1; - bitcount = 0; - bytecount = bytecount + 1; - spi_action; - end + 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/spiflash/spiflash_tb.v b/spiflash/spiflash_tb.v new file mode 100644 index 0000000..b739045 --- /dev/null +++ b/spiflash/spiflash_tb.v @@ -0,0 +1,363 @@ +/* + * A simple test bench for the SPI flash simulation model + * + * Copyright (C) 2017 Clifford 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 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