summaryrefslogtreecommitdiffstats
path: root/zpu
diff options
context:
space:
mode:
authoroharboe <oharboe>2008-09-23 19:19:08 +0000
committeroharboe <oharboe>2008-09-23 19:19:08 +0000
commit07df0bc85fffae0e271cbad3fd43260b486baebc (patch)
treee6574464c45b83b90f97e221aa4af8caa5c811d8 /zpu
parent9c7083205eed686d65dad3420b44838685a73875 (diff)
downloadzpu-07df0bc85fffae0e271cbad3fd43260b486baebc.zip
zpu-07df0bc85fffae0e271cbad3fd43260b486baebc.tar.gz
Álvaro Lopes <alvieboy@alvie.com> spi controller
Diffstat (limited to 'zpu')
-rw-r--r--zpu/hdl/spi/spi_controller.v235
1 files changed, 235 insertions, 0 deletions
diff --git a/zpu/hdl/spi/spi_controller.v b/zpu/hdl/spi/spi_controller.v
new file mode 100644
index 0000000..b22f294
--- /dev/null
+++ b/zpu/hdl/spi/spi_controller.v
@@ -0,0 +1,235 @@
+/*
+ SPI flash read-only controller
+
+ Copyright 2008 Álvaro Lopes <alvieboy@alvie.com>
+
+ Version: 1.3
+
+ The FreeBSD license
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Changelog:
+
+ 1.3: Remove async reset from spi_data shift register
+ Fix indentation of code
+
+ 1.2: Fix read count for sequential fetch
+
+ 1.1: Move port types outside module declaration.
+ Fix state machine to handle clock stop
+ Remove err out report
+ Fix SPI_CLK generation.
+*/
+
+module spi_controller (
+ clk, // Clock
+ rst, // Reset
+ ce, // Chip Enable
+ ack, // Acknowledge
+
+ adr, // Address in
+ dat_o, // Data out
+
+ SPI_MOSI, // Master Out/Slave In for SPI
+ SPI_MISO, // Master In/Slave Out for SPI
+ SPI_CLK, // SPI clock
+ SPI_SELN // SPI nSEL
+);
+
+parameter Tp = 0; // Propagation delay - for simulation
+parameter INIT_CLOCK_CYCLE_WAIT = 2; // Clock cycles to wait before init
+parameter DESELECT_CYCLES = 3; // Clock cycles to wait after deselection - should give 100ns at least
+parameter SPI_REGISTER_SIZE = 40;
+parameter SPI_ADDRESS_SIZE = 24;
+
+input clk;
+input rst;
+input ce;
+output reg ack;
+
+input [SPI_ADDRESS_SIZE-1:0] adr;
+output reg [31:0] dat_o;
+
+output reg SPI_MOSI;
+input SPI_MISO;
+output SPI_CLK;
+output reg SPI_SELN;
+
+
+// FSM states
+localparam SPI_STATE_WAIT = 7'b0000001,
+ SPI_STATE_IDLE = 7'b0000010,
+ SPI_STATE_WACK = 7'b0000100,
+ SPI_STATE_SEND = 7'b0001000,
+ SPI_STATE_BREAD = 7'b0010000,
+ SPI_STATE_READ = 7'b0100000,
+ SPI_STATE_WDES = 7'b1000000;
+
+// SPI commands
+localparam SPI_CMD_READ_FAST = 8'b00001011;
+
+
+// Shift register to hold command to be sent to SPI
+reg [SPI_REGISTER_SIZE-1:0] spi_shift_register_out;
+
+integer spi_reg_count;
+
+reg [8:0] spi_read_count;
+reg [7:0] spi_data;
+reg [3:0] data_valid_window;
+reg [SPI_REGISTER_SIZE-1:0] next_address;
+
+integer dsel_dly;
+integer spi_init_count;
+reg spi_start_count;
+reg spi_enable_clock; // Enable SPI clock
+reg [6:0] spi_state; // SPI state machine
+
+/*
+ SPI clock generation
+*/
+
+assign SPI_CLK = spi_enable_clock?~clk:1'b0;
+
+reg seq_read; // Sequential read in progress
+
+always @(posedge clk or posedge rst)
+begin
+ if ( rst ) begin
+ spi_enable_clock <= #Tp 1'b0;
+ spi_state <= #Tp SPI_STATE_WAIT;
+ spi_init_count <= #Tp INIT_CLOCK_CYCLE_WAIT;
+ SPI_SELN <= #Tp 1'b1;
+ ack <= #Tp 1'b0;
+ spi_start_count <= #Tp 1'b0;
+ next_address <= #Tp 32'hFFFFFFFF;
+ end else begin
+
+ case (spi_state)
+ SPI_STATE_WAIT:
+ begin
+ if ( spi_init_count == 0 ) begin
+ spi_state <= SPI_STATE_IDLE;
+ end else begin
+ spi_init_count <= #Tp spi_init_count - 1;
+ end
+ end
+ SPI_STATE_IDLE:
+ begin
+
+ if ( ce ) begin
+ next_address <= { adr[SPI_ADDRESS_SIZE-1:2], 2'b0 } + 4;
+ seq_read = adr[SPI_ADDRESS_SIZE-1:2] == next_address[SPI_ADDRESS_SIZE-1:2];
+ // Latch address (24 bit wordsize)
+ spi_shift_register_out <= #Tp { SPI_CMD_READ_FAST, adr[SPI_ADDRESS_SIZE-1:2], 2'b0, 8'b0 };
+
+ spi_enable_clock <= #Tp 1'b1;
+
+ if ( seq_read ) begin
+ spi_state <= #Tp SPI_STATE_BREAD;
+ end else begin
+ SPI_SELN <= 1'b1 ;
+ spi_reg_count <= #Tp SPI_REGISTER_SIZE;
+ dsel_dly <= DESELECT_CYCLES;
+ spi_state <= #Tp SPI_STATE_WDES;
+ end
+ end
+ end
+ SPI_STATE_WACK:
+ begin
+ ack <= 1'b0;
+ spi_state <= SPI_STATE_IDLE;
+ end
+ SPI_STATE_SEND:
+ begin
+
+ SPI_SELN <=#Tp 1'b0;
+
+ if (spi_reg_count == 0)
+ begin
+ spi_state <= #Tp SPI_STATE_BREAD;
+ end else begin
+ SPI_MOSI <= #Tp spi_shift_register_out[SPI_REGISTER_SIZE-1];
+ spi_shift_register_out <= #Tp { spi_shift_register_out[SPI_REGISTER_SIZE-2:0], 1'b0 };
+ spi_reg_count <= #Tp spi_reg_count - 1;
+ end
+ end
+ SPI_STATE_BREAD:
+ begin
+ spi_start_count <= #Tp 1'b1;
+ spi_state <= #Tp SPI_STATE_READ;
+ end
+ SPI_STATE_READ:
+ begin
+ spi_start_count <= #Tp 1'b0;
+
+ // Stop clock a bit earlier
+ if ( data_valid_window[3] && spi_read_count[1] )
+ spi_enable_clock <= 1'b0;
+
+ if (spi_read_count[0])
+ begin
+
+ dat_o <= #Tp { dat_o[23:0], spi_data };
+
+ if ( data_valid_window[3] ) begin
+ ack <= #Tp 1'b1;
+ spi_state <= #Tp SPI_STATE_WACK;
+ end
+ end
+ end
+ SPI_STATE_WDES:
+ begin
+ if ( dsel_dly == 0 )
+ spi_state <= SPI_STATE_SEND;
+ else
+ dsel_dly <= dsel_dly -1;
+ end
+ endcase
+ end
+end
+
+always @(posedge clk)
+begin
+ if (spi_start_count) begin
+ spi_read_count <= 8'b01000000;
+ data_valid_window <= #Tp 5'b00001;
+ end else begin
+ if ( spi_read_count[0] ) begin
+ data_valid_window <= #Tp { data_valid_window[3:0], 1'b0 };
+ end
+ spi_read_count <= #Tp { spi_read_count[0] ,spi_read_count[7:1] };
+ end
+end
+
+// SPI data shift register
+
+always @(negedge clk)
+begin
+ spi_data <= #Tp { spi_data[6:0], SPI_MISO };
+end
+
+endmodule
OpenPOWER on IntegriCloud