diff options
Diffstat (limited to 'picosoc/spiflash.v')
-rw-r--r-- | picosoc/spiflash.v | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/picosoc/spiflash.v b/picosoc/spiflash.v new file mode 100644 index 0000000..22b337b --- /dev/null +++ b/picosoc/spiflash.v @@ -0,0 +1,409 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com> + * + * 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. + * + */ + +`timescale 1 ns / 1 ps + +// +// Simple SPI flash simulation model +// +// This model samples io input signals 1ns before the SPI clock edge and +// updates output signals 1ns after the SPI clock edge. +// +// Supported commands: +// AB, B9, FF, 03, BB, EB, ED +// +// Well written SPI flash data sheets: +// Cypress S25FL064L http://www.cypress.com/file/316661/download +// Cypress S25FL128L http://www.cypress.com/file/316171/download +// +// SPI flash used on iCEBreaker board: +// https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf +// + +module spiflash ( + input csb, + input clk, + inout io0, // MOSI + inout io1, // MISO + inout io2, + inout io3 +); + localparam verbose = 0; + localparam integer latency = 8; + + 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 powered_up = 0; + + localparam [3:0] mode_spi = 1; + localparam [3:0] mode_dspi_rd = 2; + localparam [3:0] mode_dspi_wr = 3; + localparam [3:0] mode_qspi_rd = 4; + localparam [3:0] mode_qspi_wr = 5; + localparam [3:0] mode_qspi_ddr_rd = 6; + localparam [3:0] mode_qspi_ddr_wr = 7; + + 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 #1 io0 = io0_oe ? io0_dout : 1'bz; + assign #1 io1 = io1_oe ? io1_dout : 1'bz; + assign #1 io2 = io2_oe ? io2_dout : 1'bz; + assign #1 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]; + + reg [1023:0] firmware_file; + initial begin + if (!$value$plusargs("firmware=%s", firmware_file)) + firmware_file = "firmware.hex"; + $readmemh(firmware_file, 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) + xip_cmd = 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 bb) begin + if (bytecount == 1) + mode = mode_dspi_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_dspi_wr; + dummycount = latency; + end + + if (bytecount >= 5) 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 = latency; + 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 = latency; + 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_dspi_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_dspi_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 0; + io3_oe = 0; + io0_dout = buffer[6]; + 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_dspi_rd, mode_dspi_wr: begin + buffer = {buffer, io1, io0}; + bitcount = bitcount + 2; + 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 |