URL
https://opencores.org/ocsvn/qspiflash/qspiflash/trunk
Subversion Repositories qspiflash
Compare Revisions
- This comparison shows the changes necessary to convert path
/qspiflash
- from Rev 6 to Rev 7
- ↔ Reverse comparison
Rev 6 → Rev 7
/trunk/rtl/flash_config.v
0,0 → 1,48
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: flashconfig.v |
// |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: A configuration file, separated from the controller file, so |
// that multiple files can use the same wishbone Quad Spi Flash |
// controller, while each having a separate configuration. Currently, |
// the configuration only includes whether the flash is read only or not. |
// Other configuration options may be added later. |
// |
// |
// Creator: Dan Gisselquist |
// Gisselquist Tecnology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`ifndef FLASH_CONFIG_V |
`define FLASH_CONFIG_V |
// |
// `define READ_ONLY |
// |
`endif |
// |
/trunk/rtl/wbqspiflash.v
51,37 → 51,46
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`include "flash_config.v" |
// |
`define WBQSPI_RESET 0 |
`define WBQSPI_RESET_QUADMODE 1 |
`define WBQSPI_IDLE 2 |
`define WBQSPI_RDIDLE 3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 4 |
`define WBQSPI_WAIT_WIP_CLEAR 5 |
`define WBQSPI_CHECK_WIP_CLEAR 6 |
`define WBQSPI_CHECK_WIP_DONE 7 |
`define WBQSPI_WEN 8 |
`define WBQSPI_PP 9 // Program page |
`define WBQSPI_QPP 10 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 11 |
`define WBQSPI_WR_BUS_CYCLE 12 |
`define WBQSPI_RD_DUMMY 13 |
`define WBQSPI_QRD_ADDRESS 14 |
`define WBQSPI_QRD_DUMMY 15 |
`define WBQSPI_READ_CMD 16 |
`define WBQSPI_READ_DATA 17 |
`define WBQSPI_WAIT_TIL_RDIDLE 18 |
`define WBQSPI_READ_ID_CMD 19 |
`define WBQSPI_READ_ID 20 |
`define WBQSPI_READ_STATUS 21 |
`define WBQSPI_READ_CONFIG 22 |
`define WBQSPI_WRITE_STATUS 23 |
`define WBQSPI_WRITE_CONFIG 24 |
`define WBQSPI_ERASE_WEN 25 |
`define WBQSPI_ERASE_CMD 26 |
`define WBQSPI_ERASE_BLOCK 27 |
`define WBQSPI_CLEAR_STATUS 28 |
`define WBQSPI_IDLE_CHECK_WIP 29 |
`define WBQSPI_WAIT_TIL_IDLE 30 |
`define WBQSPI_RD_DUMMY 5 |
`define WBQSPI_QRD_ADDRESS 6 |
`define WBQSPI_QRD_DUMMY 7 |
`define WBQSPI_READ_CMD 8 |
`define WBQSPI_READ_DATA 9 |
`define WBQSPI_WAIT_TIL_RDIDLE 10 |
`define WBQSPI_READ_ID_CMD 11 |
`define WBQSPI_READ_ID 12 |
`define WBQSPI_READ_STATUS 13 |
`define WBQSPI_READ_CONFIG 14 |
`define WBQSPI_WAIT_TIL_IDLE 15 |
// |
// |
`ifndef READ_ONLY |
// |
`define WBQSPI_WAIT_WIP_CLEAR 16 |
`define WBQSPI_CHECK_WIP_CLEAR 17 |
`define WBQSPI_CHECK_WIP_DONE 18 |
`define WBQSPI_WEN 19 |
`define WBQSPI_PP 20 // Program page |
`define WBQSPI_QPP 21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 22 |
`define WBQSPI_WR_BUS_CYCLE 23 |
`define WBQSPI_WRITE_STATUS 24 |
`define WBQSPI_WRITE_CONFIG 25 |
`define WBQSPI_ERASE_WEN 26 |
`define WBQSPI_ERASE_CMD 27 |
`define WBQSPI_ERASE_BLOCK 28 |
`define WBQSPI_CLEAR_STATUS 29 |
`define WBQSPI_IDLE_CHECK_WIP 30 |
// |
`endif |
|
module wbqspiflash(i_clk_100mhz, |
// Internal wishbone connections |
92,10 → 101,11
// Quad Spi connections to the external device |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
o_interrupt); |
parameter ADDRESS_WIDTH=22; |
input i_clk_100mhz; |
// Wishbone, inputs first |
input i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input [19:0] i_wb_addr; |
input [(ADDRESS_WIDTH-3):0] i_wb_addr; |
input [31:0] i_wb_data; |
// then outputs |
output reg o_wb_ack; |
108,6 → 118,7
input [3:0] i_qspi_dat; |
// Interrupt line |
output reg o_interrupt; |
// output wire [31:0] o_debug; |
|
reg spi_wr, spi_hold, spi_spd, spi_dir; |
reg [31:0] spi_in; |
117,6 → 128,7
wire w_qspi_sck, w_qspi_cs_n; |
wire [3:0] w_qspi_dat; |
wire [1:0] w_qspi_mod; |
// wire [22:0] spi_dbg; |
llqspi lldriver(i_clk_100mhz, |
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir, |
spi_out, spi_valid, spi_busy, |
125,11 → 137,11
|
// Erase status tracking |
reg write_in_progress, write_protect; |
reg [5:0] erased_sector; |
reg [(ADDRESS_WIDTH-17):0] erased_sector; |
reg dirty_sector; |
initial begin |
write_in_progress = 1'b0; |
erased_sector = 6'h00; |
erased_sector = 0; |
dirty_sector = 1'b1; |
write_protect = 1'b1; |
end |
137,13 → 149,15
reg [7:0] last_status; |
reg quad_mode_enabled; |
reg spif_cmd, spif_override; |
reg [19:0] spif_addr; |
reg [(ADDRESS_WIDTH-3):0] spif_addr; |
reg [31:0] spif_data; |
reg [5:0] state; |
reg spif_ctrl, spif_req; |
wire [5:0] spif_sector; |
assign spif_sector = spif_addr[19:14]; |
wire [(ADDRESS_WIDTH-17):0] spif_sector; |
assign spif_sector = spif_addr[(ADDRESS_WIDTH-3):14]; |
|
// assign o_debug = { spi_wr, spi_spd, spi_hold, state, spi_dbg }; |
|
initial state = `WBQSPI_RESET; |
initial o_wb_ack = 1'b0; |
initial o_wb_stall = 1'b1; |
215,6 → 229,11
|
if (i_wb_we) // Request to write a page |
begin |
`ifdef READ_ONLY |
o_wb_ack <= 1'b1; |
o_wb_stall <= 1'b0; |
end else |
`else |
if((~write_protect)&&(~write_in_progress)) |
begin // 00 |
spi_wr <= 1'b1; |
239,6 → 258,7
o_wb_stall <= 1'b1; |
end |
end else if (~write_in_progress) |
`endif |
begin // Read access, normal mode(s) |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
245,14 → 265,19
spi_wr <= 1'b1; // Write cmd to device |
if (quad_mode_enabled) |
begin |
spi_in <= { 8'heb, 2'b00, i_wb_addr[19:0], 2'b00 }; |
spi_in <= { 8'heb, |
{(24-ADDRESS_WIDTH){1'b0}}, |
i_wb_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_QRD_ADDRESS; |
spi_len <= 2'b00; // single byte, cmd only |
end else begin |
spi_in <= { 8'h0b, 2'b00, i_wb_addr[19:0], 2'b00 }; |
spi_in <= { 8'h0b, |
{(24-ADDRESS_WIDTH){1'b0}}, |
i_wb_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_RD_DUMMY; |
spi_len <= 2'b11; // cmd+addr,32bits |
end |
`ifndef READ_ONLY |
end else begin |
// A write is in progress ... need to stall |
// the bus until the write is complete. |
259,9 → 284,14
state <= `WBQSPI_WAIT_WIP_CLEAR; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
`endif |
end |
end else if ((i_wb_cyc)&&(i_wb_ctrl_stb)&&(i_wb_we)) |
begin |
`ifdef READ_ONLY |
o_wb_ack <= 1'b1; |
o_wb_stall <= 1'b0; |
`else |
o_wb_stall <= 1'b1; |
case(i_wb_addr[1:0]) |
2'b00: begin // Erase command register |
321,6 → 351,7
o_wb_stall <= 1'b0; |
end |
endcase |
`endif |
end else if ((i_wb_cyc)&&(i_wb_ctrl_stb)) // &&(~i_wb_we)) |
begin |
case(i_wb_addr[1:0]) |
341,7 → 372,7
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
7'h00, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end end |
2'b01: begin // Read configuration register |
372,6 → 403,7
o_wb_stall <= 1'b1; |
end |
endcase |
`ifndef READ_ONLY |
end else if ((~i_wb_cyc)&&(write_in_progress)) |
begin |
state <= `WBQSPI_IDLE_CHECK_WIP; |
381,6 → 413,7
|
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
`endif |
end |
end else if (state == `WBQSPI_RDIDLE) |
begin |
400,7 → 433,8
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
spi_len <= 2'b10; // Write address, but not mode byte |
spi_in <= { 2'h0, i_wb_addr[19:0], 2'h0, 8'ha0 }; |
spi_in <= { {(24-ADDRESS_WIDTH){1'b0}}, |
i_wb_addr[(ADDRESS_WIDTH-3):0], 2'b00, 8'ha0 }; |
state <= `WBQSPI_QRD_DUMMY; |
end else if((i_wb_cyc)&&(i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
begin |
412,7 → 446,7
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
7'h00, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end else if((i_wb_cyc)&&((i_wb_ctrl_stb)||(i_wb_data_stb))) |
begin // Need to release the device from quad mode for all else |
445,6 → 479,11
begin |
if (spif_cmd) // Request to write a page |
begin |
`ifdef READ_ONLY |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
`else |
if((~write_protect)&&(~write_in_progress)) |
begin // 00 |
spi_wr <= 1'b1; |
471,13 → 510,20
end |
// end else if (~write_in_progress) // always true |
// but ... we wouldn't get here on a normal read access |
`endif |
end else begin |
// Something's wrong, we should never get here |
// Something's wrong, we should never |
// get here |
// Attempt to go to idle to recover |
state <= `WBQSPI_IDLE; |
end |
end else if ((spif_ctrl)&&(spif_cmd)) |
begin |
`ifdef READ_ONLY |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
`else |
o_wb_stall <= 1'b1; |
case(spif_addr[1:0]) |
2'b00: begin // Erase command register |
529,6 → 575,7
state <= `WBQSPI_IDLE; |
end |
endcase |
`endif |
end else begin // on (~spif_we) |
case(spif_addr[1:0]) |
2'b00: begin // Read local register |
568,203 → 615,10
endcase |
end |
end |
end else if (state == `WBQSPI_WAIT_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~spi_busy) |
begin |
spi_wr <= 1'b1; |
spi_in <= { 8'h05, 24'h0000 }; |
spi_hold <= 1'b1; |
spi_len <= 2'b01; // 16 bits write, so we can read 8 |
state <= `WBQSPI_CHECK_WIP_CLEAR; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
end |
end else if (state == `WBQSPI_CHECK_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Repeat as often as necessary until we are clear |
spi_wr <= 1'b1; |
spi_in <= 32'h0000; // Values here are actually irrelevant |
spi_hold <= 1'b1; |
spi_len <= 2'b00; // One byte at a time |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((spi_valid)&&(~spi_out[0])) |
begin |
state <= `WBQSPI_CHECK_WIP_DONE; |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
write_in_progress <= 1'b0; |
last_status <= spi_out[7:0]; |
end |
end else if (state == `WBQSPI_CHECK_WIP_DONE) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Let's let the SPI port come back to a full idle, |
// and the chip select line go low before continuing |
spi_wr <= 1'b0; |
spi_len <= 2'b00; |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((o_qspi_cs_n)&&(~spi_busy)) // Chip select line is high, we can continue |
begin |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
|
casez({ spif_cmd, spif_ctrl, spif_addr[1:0] }) |
4'b00??: begin // Read data from ... somewhere |
spi_wr <= 1'b1; // Write cmd to device |
if (quad_mode_enabled) |
begin |
spi_in <= { 8'heb, 2'b00, spif_addr[19:0], 2'b00 }; |
state <= `WBQSPI_QRD_ADDRESS; |
// spi_len <= 2'b00; // single byte, cmd only |
end else begin |
spi_in <= { 8'h0b, 2'b00, spif_addr[19:0], 2'b00 }; |
state <= `WBQSPI_RD_DUMMY; |
spi_len <= 2'b11; // Send cmd and addr |
end end |
4'b10??: begin // Write data to ... anywhere |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WEN; |
end |
4'b0110: begin // Read status register |
state <= `WBQSPI_READ_STATUS; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; // 8 bits out, 8 bits in |
spi_in <= { 8'h05, 24'h00}; |
end |
4'b0111: begin |
state <= `WBQSPI_READ_ID_CMD; |
spi_wr <= 1'b1; |
spi_len <= 2'b00; |
spi_in <= { 8'h9f, 24'h00}; |
end |
default: begin // |
o_wb_stall <= 1'b1; |
o_wb_ack <= spif_req; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end |
endcase |
// spif_cmd <= i_wb_we; |
// spif_addr <= i_wb_addr; |
// spif_data <= i_wb_data; |
// spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb); |
// spi_wr <= 1'b0; // Keep the port idle, unless told otherwise |
end |
end else if (state == `WBQSPI_WEN) |
begin // We came here after issuing a write enable command |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)) // Let's come to a full stop |
state <= (quad_mode_enabled)?`WBQSPI_QPP:`WBQSPI_PP; |
// state <= `WBQSPI_PP; |
end else if (state == `WBQSPI_PP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
spi_in <= { 8'h02, 2'h0, spif_addr, 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
state <= `WBQSPI_WR_DATA; |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_QPP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
spi_in <= { 8'h32, 2'h0, spif_addr, 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
begin |
// spi_wr is irrelevant here ... |
// Set the speed value once, but wait til we get busy |
// to do so. |
spi_spd <= 1'b1; |
state <= `WBQSPI_WR_DATA; |
end |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_WR_DATA) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b1; // write without waiting |
spi_in <= { |
spif_data[ 7: 0], |
spif_data[15: 8], |
spif_data[23:16], |
spif_data[31:24] }; |
spi_len <= 2'b11; // Write 4 bytes |
spi_hold <= 1'b1; |
if (~spi_busy) |
begin |
o_wb_ack <= spif_req; // Ack when command given |
state <= `WBQSPI_WR_BUS_CYCLE; |
end |
spif_req<= (spif_req) && (i_wb_cyc); |
end else if (state == `WBQSPI_WR_BUS_CYCLE) |
begin |
o_wb_ack <= 1'b0; // Turn off our ack and stall flags |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b0; |
spi_hold <= 1'b1; |
write_in_progress <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~i_wb_cyc) |
begin |
state <= `WBQSPI_WAIT_TIL_IDLE; |
spi_hold <= 1'b0; |
end else if (spi_wr) |
begin // Give the SPI a chance to get busy on the last write |
// Do nothing here. |
end else if ((i_wb_data_stb)&&(i_wb_we) |
&&(i_wb_addr == (spif_addr+1)) |
&&(i_wb_addr[19:6]==spif_addr[19:6])) |
begin |
spif_cmd <= 1'b1; |
spif_data <= i_wb_data; |
spif_addr <= i_wb_addr; |
spif_ctrl <= 1'b0; |
spif_req<= 1'b1; |
// We'll keep the bus stalled on this request |
// for a while |
state <= `WBQSPI_WR_DATA; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b0; |
end else if ((i_wb_data_stb|i_wb_ctrl_stb)&&(~o_wb_ack)) // Writing out of bounds |
begin |
spi_hold <= 1'b0; |
spi_wr <= 1'b0; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end // Otherwise we stay here |
// |
// |
// READ DATA section: for both data and commands |
// |
end else if (state == `WBQSPI_RD_DUMMY) |
begin |
o_wb_ack <= 1'b0; |
791,7 → 645,8
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b1; // Non-stop |
spi_in <= { 2'b0, spif_addr, 2'b0, 8'ha0 }; |
spi_in <= { {(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00, 8'ha0 }; |
spi_len <= 2'b10; // Write address, not mode byte |
spi_spd <= 1'b1; |
spi_dir <= 1'b0; // Still writing |
950,7 → 805,7
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
7'h00, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end else begin |
o_wb_data <= { 24'h00, spi_out[7:0] }; |
981,6 → 836,217
o_wb_stall <= 1'b0; |
spif_req <= 1'b0; |
end |
|
// |
// |
// Write/erase data section |
// |
`ifndef READ_ONLY |
end else if (state == `WBQSPI_WAIT_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~spi_busy) |
begin |
spi_wr <= 1'b1; |
spi_in <= { 8'h05, 24'h0000 }; |
spi_hold <= 1'b1; |
spi_len <= 2'b01; // 16 bits write, so we can read 8 |
state <= `WBQSPI_CHECK_WIP_CLEAR; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
end |
end else if (state == `WBQSPI_CHECK_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Repeat as often as necessary until we are clear |
spi_wr <= 1'b1; |
spi_in <= 32'h0000; // Values here are actually irrelevant |
spi_hold <= 1'b1; |
spi_len <= 2'b00; // One byte at a time |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((spi_valid)&&(~spi_out[0])) |
begin |
state <= `WBQSPI_CHECK_WIP_DONE; |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
write_in_progress <= 1'b0; |
last_status <= spi_out[7:0]; |
end |
end else if (state == `WBQSPI_CHECK_WIP_DONE) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Let's let the SPI port come back to a full idle, |
// and the chip select line go low before continuing |
spi_wr <= 1'b0; |
spi_len <= 2'b00; |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((o_qspi_cs_n)&&(~spi_busy)) // Chip select line is high, we can continue |
begin |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
|
casez({ spif_cmd, spif_ctrl, spif_addr[1:0] }) |
4'b00??: begin // Read data from ... somewhere |
spi_wr <= 1'b1; // Write cmd to device |
if (quad_mode_enabled) |
begin |
spi_in <= { 8'heb, |
{(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_QRD_ADDRESS; |
// spi_len <= 2'b00; // single byte, cmd only |
end else begin |
spi_in <= { 8'h0b, |
{(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_RD_DUMMY; |
spi_len <= 2'b11; // Send cmd and addr |
end end |
4'b10??: begin // Write data to ... anywhere |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WEN; |
end |
4'b0110: begin // Read status register |
state <= `WBQSPI_READ_STATUS; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; // 8 bits out, 8 bits in |
spi_in <= { 8'h05, 24'h00}; |
end |
4'b0111: begin |
state <= `WBQSPI_READ_ID_CMD; |
spi_wr <= 1'b1; |
spi_len <= 2'b00; |
spi_in <= { 8'h9f, 24'h00}; |
end |
default: begin // |
o_wb_stall <= 1'b1; |
o_wb_ack <= spif_req; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end |
endcase |
// spif_cmd <= i_wb_we; |
// spif_addr <= i_wb_addr; |
// spif_data <= i_wb_data; |
// spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb); |
// spi_wr <= 1'b0; // Keep the port idle, unless told otherwise |
end |
end else if (state == `WBQSPI_WEN) |
begin // We came here after issuing a write enable command |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)) // Let's come to a full stop |
state <= (quad_mode_enabled)?`WBQSPI_QPP:`WBQSPI_PP; |
// state <= `WBQSPI_PP; |
end else if (state == `WBQSPI_PP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
spi_in <= { 8'h02, |
{(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
state <= `WBQSPI_WR_DATA; |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_QPP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
spi_in <= { 8'h32, |
{(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
begin |
// spi_wr is irrelevant here ... |
// Set the speed value once, but wait til we get busy |
// to do so. |
spi_spd <= 1'b1; |
state <= `WBQSPI_WR_DATA; |
end |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_WR_DATA) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b1; // write without waiting |
spi_in <= { |
spif_data[ 7: 0], |
spif_data[15: 8], |
spif_data[23:16], |
spif_data[31:24] }; |
spi_len <= 2'b11; // Write 4 bytes |
spi_hold <= 1'b1; |
if (~spi_busy) |
begin |
o_wb_ack <= spif_req; // Ack when command given |
state <= `WBQSPI_WR_BUS_CYCLE; |
end |
spif_req<= (spif_req) && (i_wb_cyc); |
end else if (state == `WBQSPI_WR_BUS_CYCLE) |
begin |
o_wb_ack <= 1'b0; // Turn off our ack and stall flags |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b0; |
spi_hold <= 1'b1; |
write_in_progress <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~i_wb_cyc) |
begin |
state <= `WBQSPI_WAIT_TIL_IDLE; |
spi_hold <= 1'b0; |
end else if (spi_wr) |
begin // Give the SPI a chance to get busy on the last write |
// Do nothing here. |
end else if ((i_wb_data_stb)&&(i_wb_we) |
&&(i_wb_addr == (spif_addr+1)) |
&&(i_wb_addr[(ADDRESS_WIDTH-3):6]==spif_addr[(ADDRESS_WIDTH-3):6])) |
begin |
spif_cmd <= 1'b1; |
spif_data <= i_wb_data; |
spif_addr <= i_wb_addr; |
spif_ctrl <= 1'b0; |
spif_req<= 1'b1; |
// We'll keep the bus stalled on this request |
// for a while |
state <= `WBQSPI_WR_DATA; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b0; |
end else if ((i_wb_data_stb|i_wb_ctrl_stb)&&(~o_wb_ack)) // Writing out of bounds |
begin |
spi_hold <= 1'b0; |
spi_wr <= 1'b0; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end // Otherwise we stay here |
end else if (state == `WBQSPI_WRITE_CONFIG) |
begin // We enter immediately after commanding a WEN |
o_wb_ack <= 1'b0; |
1036,7 → 1102,7
// together with setting our copy of the WIP bit |
write_in_progress <= 1'b1; |
// keeping track of which sector we just erased |
erased_sector <= spif_data[19:14]; |
erased_sector <= spif_data[(ADDRESS_WIDTH-3):14]; |
// and marking this erase sector as no longer dirty |
dirty_sector <= 1'b0; |
|
1100,6 → 1166,7
o_wb_ack <= 1'b0; |
state <= `WBQSPI_IDLE; |
end |
`endif // !READ_ONLY |
end else // if (state == `WBQSPI_WAIT_TIL_IDLE) or anything else |
begin |
spi_wr <= 1'b0; |
/trunk/rtl/llqspi.v
76,6 → 76,10
output reg [3:0] o_dat; |
input [3:0] i_dat; |
|
// output wire [22:0] o_dbg; |
// assign o_dbg = { state, spi_len, |
// o_busy, o_valid, o_cs_n, o_sck, o_mod, o_dat, i_dat }; |
|
// Timing: |
// |
// Tick Clk BSY/WR CS_n BIT/MO STATE |