Last active
December 5, 2018 01:32
-
-
Save buttercutter/340152cd4f62aec0df53052273193920 to your computer and use it in GitHub Desktop.
SPI flash controller for Winbond W25Q32 flash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module test_SPI(clk, rst, serial_in, serial_out, o_busy_n, rx_error_n, MISO, MOSI); | |
parameter MOSI_DATA_BITWIDTH = 8 + 24 + 256*8; // to accomodate the maximum bit length for page_program(02h) | |
parameter MISO_DATA_BITWIDTH = 8; | |
localparam UART_LENGTH = 8; // 8N1 uart system | |
localparam NUM_OF_MUX_BIT = 1; | |
// hex ASCII code | |
localparam START_CHARACTER = 'h7F; // del character | |
localparam INSTRUCTION_CHARACTER = 'h53; // 'S' | |
localparam ADDRESS_SIGNAL = 'h41; | |
localparam DATA_SIGNAL = 'h44; | |
localparam INTERRUPT_SIGNAL = 'h49; | |
localparam ACKNOWLEDGE_SIGNAL = 'h4B; | |
localparam RESET_SIGNAL = 'h54; | |
input clk, rst; | |
input serial_in; | |
output serial_out, o_busy_n, rx_error_n; // busy and error signals are negatively asserted due to LED diode terminal connection on my own FPGA board schematics | |
input MISO; | |
output MOSI; | |
wire CS_flash; | |
reg clk_flash; | |
wire [UART_LENGTH-1:0] uart_tx_data; | |
wire [UART_LENGTH-1:0] received_data; // UART Rx data | |
reg [UART_LENGTH-1:0] received_data_previous; | |
wire data_ready, data_valid; | |
wire [MISO_DATA_BITWIDTH-1:0] data_MISO; | |
reg [$clog2(MOSI_DATA_BITWIDTH)-1:0] data_MOSI; | |
// for power-up hardware reset as well as HOST(linux) software reset | |
wire reset = ((rst & !rst_previous) || ((received_data[0 +: UART_LENGTH-1-NUM_OF_MUX_BIT] == RESET_SIGNAL) && (received_data_previous[0 +: UART_LENGTH-1-NUM_OF_MUX_BIT] == START_CHARACTER))) ? 1 : 0; | |
reg rst_previous; | |
always @(posedge clk) rst_previous <= rst; | |
always @(posedge clk) received_data_previous <= received_data; | |
wire o_busy, rx_error; | |
assign o_busy_n = !o_busy; | |
assign rx_error_n = !rx_error; | |
UART uart(.tx_clk(clk), .rx_clk(clk), .reset_tx(reset), .reset_rx(reset), .serial_out(serial_out), .enable(data_ready), .i_data(uart_tx_data), .o_busy(o_busy), .serial_in(serial_in), .received_data(received_data), .data_is_valid(data_valid), .rx_error(rx_error)); | |
// SPI flash controller | |
SPI spi(.clk(clk), .reset(reset), .data_valid(data_valid), .data_MOSI(data_MOSI), .data_ready(data_ready), .data_MISO(data_MISO), .MISO(MISO), .MOSI(MOSI), .clk_flash(clk_flash), .CS_flash(CS_flash)); | |
// UART-to-SPI bridge details | |
/* | |
MUX is used to multiplex two different device endpoints. | |
MUX = 0 is used as console debug port | |
MUX = 1 is used for SPI-related transactions | |
Possible COMMAND: 'A'ddress, 'D'ata, 'I'nterrupt, ac'K'owledge, in'S'truction, rese'T' | |
Possible DATA range: all 7-bit binary characters (except DEL character) listed in the standard ASCII table | |
this scheme uses "NUM_OF_BYTES" which is basically the number of bytes required for page_program(02h) and read_data(03h) instructions | |
{1-bit MUX, 7-bit COMMAND or DATA in ASCII hex} occupy 8N1 UART | |
To start a transaction across the bridge, send the DEL character(which is of hex value 7F) first. | |
Then, send the in'S'truction character(which is of hex value 53), followed by optional ADDRESS and/or DATA bits | |
Note: I only implement write_enable(06h), sector_erase(20h), page_program(02h), read_data(03h) and read_status_register_1(05h) instructions | |
be careful with read_data(03h) and read_status_register_1(05h) instructions in which the DO bits are shifted out at negative clock edge right after the positive clock edge of | |
the LSB of 24-bit address | |
ADDRESS is used to indicate that the following bits are of 24-bit SPI slave(flash) address. | |
INTERRUPT is sent by SPI slave (flash) to let master (FPGA) knows that erase(20h), page program(02h) or write status register(01h) instructions had finished, | |
with the use of read status register(05h) instruction for checking whether busy bit is 0 or not. | |
ACKNOWLEDGE is used to ack signals from HOST. | |
DATA is used to indicate that the following bits are data or status register for reading/writing, the bits are transmitted to/from hardware(FPGA) from/to HOST. | |
RESET is used to let hardware(FPGA) knows that HOST softwware had reset itself, so as to formally inform the FPGA to do the same reset as well in hardware. | |
*/ | |
// for page_program(02h) and read_data(03h) instructions only | |
localparam MAX_NUM_OF_DATA_BYTES_IN_A_SPI_TRANSACTION = 260; // for max data byte in page_program(02h) | |
reg [$clog2(MAX_NUM_OF_DATA_BYTES_IN_A_SPI_TRANSACTION)-1:0] TOTAL_NUM_OF_DATA_BYTES_IN_A_SPI_TRANSACTION; // dynamically determined from the information sent over the bridge | |
// bits from from hardware(FPGA) to HOST(linux) | |
always @(posedge clk) | |
begin | |
if(reset) begin | |
uart_tx_data <= {UART_LENGTH{1'b1}}; // reset to idle state | |
end | |
else begin | |
uart_tx_data <= ; | |
end | |
end | |
// bits from from HOST(linux) to hardware(FPGA) | |
always @(posedge clk) | |
begin | |
if(reset) begin | |
data_MOSI <= 0; | |
end | |
else begin | |
data_MOSI <= ; | |
end | |
end | |
always @(posedge clk) | |
begin | |
if(reset) begin | |
clk_flash <= 0; | |
end | |
else begin | |
clk_flash <= 1; | |
end | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment