123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * 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.
- *
- */
- `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 = 1;
- 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 = 1;
- 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 0b) 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 >= 5) 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
|