aboutsummaryrefslogtreecommitdiffstats
path: root/spiflash
diff options
context:
space:
mode:
Diffstat (limited to 'spiflash')
-rw-r--r--spiflash/.gitignore2
-rw-r--r--spiflash/Makefile18
-rw-r--r--spiflash/spiflash.v252
-rw-r--r--spiflash/spiflash_tb.v363
4 files changed, 609 insertions, 26 deletions
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;
+
+ 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;
- wire spi_mosi = io0;
- assign io1 = spi_miso;
+ 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 <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