URL
https://opencores.org/ocsvn/qspiflash/qspiflash/trunk
Subversion Repositories qspiflash
Compare Revisions
- This comparison shows the changes necessary to convert path
/qspiflash/trunk/rtl
- from Rev 14 to Rev 16
- ↔ Reverse comparison
Rev 14 → Rev 16
/Makefile
1,4 → 1,4
##########################################################################/ |
################################################################################ |
## |
## Filename: Makefile |
## |
12,9 → 12,9
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
##########################################################################/ |
################################################################################ |
## |
## Copyright (C) 2015, Gisselquist Technology, LLC |
## Copyright (C) 2015-2017, 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 |
26,11 → 26,16
## 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 |
## http://www.gnu.org/licenses/gpl.html |
## |
## |
##########################################################################/ |
################################################################################ |
## |
## |
all: test |
40,12 → 45,13
VDIRFB:= $(FBDIR)/obj_dir |
|
.PHONY: test |
test: $(VDIRFB)/Veqspiflash__ALL.a |
test: $(VDIRFB)/Veqspiflash__ALL.a $(VDIRFB)/Vwbqspiflash__ALL.a |
# test: $(VDIRFB)/Vfastmaster__ALL.a |
|
$(VDIRFB)/Veqspiflash.h $(VDIRFB)/Veqspiflash.cpp $(VDIRFB)/Veqspiflash.mk: eqspiflash.v lleqspi.v |
$(VDIRFB)/Vwbqspiflash.h $(VDIRFB)/Vwbqspiflash.cpp $(VDIRFB)/Vwbqspiflash.mk: wbqspiflash.v llqspi.v |
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v |
verilator -cc $*.v |
verilator -trace -cc $*.v |
|
$(VDIRFB)/V%__ALL.a: $(VDIRFB)/V%.mk |
cd $(VDIRFB); make -f V$*.mk |
/eqspiflash.v
2,7 → 2,7
// |
// Filename: eqspiflash.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: Provide access to the flash device on an Arty, via the Extended |
// SPI interface. Reads and writes will use the QuadSPI interface |
90,7 → 90,7
// 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 |
// 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. |
// |
102,7 → 102,7
// |
// |
// `define QSPI_READ_ONLY |
module eqspiflash(i_clk_200mhz, i_rst, |
module eqspiflash(i_clk_82mhz, i_rst, |
// Incoming wishbone connection(s) |
// The two strobe lines allow the data to live on a |
// separate part of the master bus from the control |
120,7 → 120,7
// Debug the interface |
o_dbg); |
|
input i_clk_200mhz, i_rst; |
input i_clk_82mhz, i_rst; |
// Wishbone bus inputs |
input i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input [21:0] i_wb_addr; // 24 bits of addr space |
142,7 → 142,7
output wire [31:0] o_dbg; |
|
initial o_cmd_accepted = 1'b0; |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
o_cmd_accepted=((i_wb_data_stb)||(i_wb_ctrl_stb))&&(~o_wb_stall); |
// |
// lleqspi |
154,7 → 154,7
reg [1:0] spi_len; |
wire [31:0] spi_out; |
wire spi_valid, spi_busy, spi_stopped; |
lleqspi lowlvl(i_clk_200mhz, spi_wr, spi_hold, spi_word, spi_len, |
lleqspi lowlvl(i_clk_82mhz, spi_wr, spi_hold, spi_word, spi_len, |
spi_spd, spi_dir, spi_recycle, spi_out, spi_valid, spi_busy, |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat); |
assign spi_stopped = (o_qspi_cs_n)&&(~spi_busy)&&(~spi_wr); |
181,7 → 181,7
// Live parameters |
w_xip, w_quad, w_idloaded, w_leave_xip; |
reg bus_wip; |
qspibus preproc(i_clk_200mhz, i_rst, |
qspibus preproc(i_clk_82mhz, i_rst, |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, |
i_wb_we, i_wb_addr, i_wb_data, |
bus_wb_ack, bus_wb_stall, bus_wb_data, |
211,7 → 211,7
wire [31:0] rd_spi_word; |
wire [1:0] rd_spi_len; |
// |
readqspi rdproc(i_clk_200mhz, bus_readreq, bus_piperd, |
readqspi rdproc(i_clk_82mhz, bus_readreq, bus_piperd, |
bus_other_req, |
bus_addr, rd_bus_ack, |
rd_qspi_req, rd_qspi_grant, |
241,7 → 241,7
// |
wire w_ew_wip; |
// |
writeqspi ewproc(i_clk_200mhz, bus_wreq,bus_ereq, |
writeqspi ewproc(i_clk_82mhz, bus_wreq,bus_ereq, |
bus_pipewr, bus_endwr, |
bus_addr, bus_data, |
ew_bus_ack, ew_qspi_req, ew_qspi_grant, |
268,7 → 268,7
wire [31:0] ct_spi_word; |
wire [1:0] ct_spi_len; |
// |
ctrlspi ctproc(i_clk_200mhz, |
ctrlspi ctproc(i_clk_82mhz, |
bus_ctreq, bus_wr, bus_addr[3:0], bus_data, bus_sector, |
ct_qspi_req, ct_grant, |
ct_spi_wr, ct_spi_hold, ct_spi_word, ct_spi_len, |
299,7 → 299,7
// |
wire w_id_wip; |
// |
idotpqspi idotp(i_clk_200mhz, bus_idreq, |
idotpqspi idotp(i_clk_82mhz, bus_idreq, |
bus_wr, bus_pipewr, bus_addr[4:0], bus_data, id_bus_ack, |
id_qspi_req, id_qspi_grant, |
id_spi_wr, id_spi_hold, id_spi_word, id_spi_len, |
311,7 → 311,7
reg owned; |
reg [1:0] owner; |
initial owned = 1'b0; |
always @(posedge i_clk_200mhz) // 7 inputs (spi_stopped is the CE) |
always @(posedge i_clk_82mhz) // 7 inputs (spi_stopped is the CE) |
if ((~owned)&&(spi_stopped)) |
begin |
casez({rd_qspi_req,ew_qspi_req,id_qspi_req,ct_qspi_req}) |
338,7 → 338,7
assign ct_grant = (owned)&&(owner == 2'b11); |
|
// Module controller |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
case(owner) |
2'b00: begin |
spi_wr <= (owned)&&(rd_spi_wr); |
382,7 → 382,7
initial bus_wip = 1'b0; |
initial last_wip = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
begin |
bus_wip <= w_ew_wip || w_id_wip; |
last_wip <= bus_wip; |
391,7 → 391,7
|
|
// Now, let's return values onto the wb bus |
always @(posedge i_clk_200mhz) |
always @(posedge i_clk_82mhz) |
begin |
// Ack our internal bus controller. This means the command was |
// accepted, and the bus can go on to looking for the next |
451,7 → 451,7
// |
reg pending, lcl_wrreq, lcl_ctreq, lcl_ack, ack, wp_err, wp; |
reg lcl_reg; |
reg [14:0] esector; |
reg [12:0] esector; |
reg [21:0] next_addr; |
|
|
583,7 → 583,7
wire new_req; |
assign new_req = (pending)&&(~last_pending); |
|
initial esector = 15'h00; |
initial esector = 13'h00; |
initial o_wrreq = 1'b0; |
initial o_erreq = 1'b0; |
initial wp_err = 1'b0; |
613,9 → 613,9
|
if (set_sector) |
begin |
esector[13:0] <= { o_data[23:14], 4'h0 }; |
esector[11:0] <= { o_data[21:14], 4'h0 }; |
wp <= (o_data[30])&&(new_req)||(wp)&&(~new_req); |
esector[14] <= o_data[28]; // Subsector |
esector[12] <= o_data[28]; // Subsector |
if (o_data[28]) |
begin |
esector[3:0] <= o_data[13:10]; |
679,10 → 679,10
end |
|
|
assign o_wb_data[31:0] = { i_wip, ~wp, i_quad, esector[14], |
assign o_wb_data[31:0] = { i_wip, ~wp, i_quad, esector[12], |
i_idloaded, wp_err, i_xip, i_spi_stopped, |
esector[13:0], 10'h00 }; |
assign o_sector = { esector[13:0], 8'h00 }; // 22 bits |
2'b00, esector[11:0], 10'h00 }; |
assign o_sector = { 2'b00, esector[11:0], 8'h00 }; // 22 bits |
assign o_other = (r_other)||(o_idreq); |
|
endmodule |
698,6 → 698,7
`define RD_XIP 4'h7 |
`define RD_GO_TO_IDLE 4'h8 |
`define RD_GO_TO_XIP 4'h9 |
`define RD_IDLE_QUAD_PORT 4'ha |
|
module readqspi(i_clk, i_readreq, i_piperd, i_other_req, i_addr, o_bus_ack, |
o_qspi_req, i_grant, |
746,11 → 747,16
`RD_IDLE: begin |
r_requested <= 1'b0; |
o_qspi_req <= 1'b0; |
o_spi_word <= { ((i_quad)? 8'h6B: 8'h0b), i_addr, 2'b00 }; |
// 0x0b is a fast read, uses all SPI protocol |
// 0x6b is a Quad output fast read, uses SPI cmd, |
// SPI address, QSPI data |
// 0xeb is a Quad I/O fast read, using SPI cmd, |
// QSPI address and data |
o_spi_word <= { ((i_quad)? 8'hEB: 8'h0b), i_addr, 2'b00 }; |
o_spi_wr <= 1'b0; |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_len <= (i_quad)? 2'b00 : 2'b11; |
r_xip <= (i_xip)&&(i_quad); |
r_leave_xip <= 1'b0; // Not in it, so can't leave it |
r_quad <= i_quad; |
763,15 → 769,30
o_spi_wr <= 1'b1; // Write the address |
o_qspi_req <= 1'b1; |
if (accepted) |
begin |
rd_state <= (r_quad) ? `RD_IDLE_QUAD_PORT : `RD_SLOW_DUMMY; |
o_spi_word[31:8] <= o_spi_word[23:0]; |
end end |
`RD_IDLE_QUAD_PORT: begin |
o_spi_wr <= 1'b1; // Write the command |
o_qspi_req <= 1'b1; |
o_spi_spd <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b10; |
|
// We haven't saved our address any where but in the |
// SPI word we just sent. Hence, let's just |
// grab it from there. |
if (accepted) |
rd_state <= `RD_SLOW_DUMMY; |
end |
`RD_SLOW_DUMMY: begin |
o_spi_wr <= 1'b1; // Write 8 dummy clocks |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; // Write 8 dummy clocks--this is the same for |
o_qspi_req <= 1'b1; // both Quad I/O, Quad O, and fast-read commands |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_word[31:24] <= (r_xip) ? 8'h00 : 8'hff; |
o_spi_len <= 2'b00; // 8 clocks = 8-bits |
o_spi_spd <= r_quad; |
o_spi_word[31:0] <= (r_xip) ? 32'h00 : 32'hffffffff; |
o_spi_len <= (r_quad)? 2'b11:2'b00; // 8 clocks |
if (accepted) |
rd_state <= (r_quad)?`RD_QUAD_READ_DATA |
: `RD_SLOW_READ_DATA; |
/flash_config.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: flashconfig.v |
// |
10,13 → 10,12
// the configuration only includes whether the flash is read only or not. |
// Other configuration options may be added later. |
// |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, 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 |
29,7 → 28,7
// 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 |
// 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. |
// |
37,8 → 36,9
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`ifndef FLASH_CONFIG_V |
`define FLASH_CONFIG_V |
// |
/lleqspi.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: lleqspi.v |
// |
10,12 → 10,12
// When not in use, unlike our previous SPI work, no bits will |
// toggle. |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, 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 |
28,7 → 28,7
// 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 |
// 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. |
// |
36,7 → 36,9
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define EQSPI_IDLE 3'h0 |
`define EQSPI_START 3'h1 |
`define EQSPI_BITS 3'h2 |
/llqspi.v
1,4 → 1,4
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: llqspi.v |
// |
10,12 → 10,12
// When not in use, unlike our previous SPI work, no bits will |
// toggle. |
// |
// Creator: Dan Gisselquist |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, 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 |
28,7 → 28,7
// 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 |
// 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. |
// |
36,7 → 36,7
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
193,6 → 193,7
initial o_valid = 1'b0; |
initial o_busy = 1'b0; |
initial r_input = 31'h000; |
initial o_mod = `QSPI_MOD_SPI; |
always @(posedge i_clk) |
if ((state == `QSPI_IDLE)&&(o_sck)) |
begin |
200,16 → 201,16
o_valid <= 1'b0; |
o_busy <= 1'b0; |
o_mod <= `QSPI_MOD_SPI; |
r_word <= i_word; |
r_spd <= i_spd; |
r_dir <= i_dir; |
if (i_wr) |
begin |
r_word <= i_word; |
state <= `QSPI_START; |
r_spd <= i_spd; |
r_dir <= i_dir; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8; |
o_cs_n <= 1'b0; |
// o_sck <= 1'b1; |
o_busy <= 1'b1; |
o_sck <= 1'b1; |
end |
end else if (state == `QSPI_START) |
begin // We come in here with sck high, stay here 'til sck is low |
228,13 → 229,9
o_busy <= 1'b1; |
o_valid <= 1'b0; |
if (r_spd) |
begin |
o_dat <= r_word[31:28]; |
// r_word <= { r_word[27:0], 4'h0 }; |
end else begin |
else |
o_dat <= { 3'b110, r_word[31] }; |
// r_word <= { r_word[30:0], 1'b0 }; |
end |
end else if (~o_sck) |
begin |
o_sck <= 1'b1; |
276,6 → 273,16
// this state. Here we chose to either STOP or |
// continue and transmit more. |
o_sck <= (i_hold); // No clocks while holding |
r_spd <= i_spd; |
r_dir <= i_dir; |
if (i_spd) |
begin |
r_word <= { i_word[27:0], 4'h0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 - 6'h4; |
end else begin |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 - 6'h1; |
end |
if((~o_busy)&&(i_wr))// Acknowledge a new request |
begin |
state <= `QSPI_BITS; |
283,51 → 290,29
o_sck <= 1'b0; |
|
// Read the new request off the bus |
r_spd <= i_spd; |
r_dir <= i_dir; |
// Set up the first bits on the bus |
o_mod <= (i_spd) ? { 1'b1, i_dir } : `QSPI_MOD_SPI; |
if (i_spd) |
begin |
o_dat <= i_word[31:28]; |
r_word <= { i_word[27:0], 4'h0 }; |
// spi_len <= spi_len - 4; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h4; |
end else begin |
else |
o_dat <= { 3'b110, i_word[31] }; |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h1; |
end |
|
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
end else begin |
o_sck <= 1'b1; |
state <= (i_hold)?`QSPI_HOLDING : `QSPI_STOP; |
o_busy <= (~i_hold); |
end |
|
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
// Read a bit upon any transition |
o_valid <= 1'b1; |
if (~o_mod[1]) |
begin |
r_input <= { r_input[29:0], i_miso }; |
o_word <= { r_input[30:0], i_miso }; |
end else if (o_mod[1]) |
begin |
r_input <= { r_input[26:0], i_dat }; |
o_word <= { r_input[27:0], i_dat }; |
end |
end else if (state == `QSPI_HOLDING) |
begin |
/wbqspiflash.v
55,45 → 55,45
`include "flash_config.v" |
`default_nettype none |
// |
`define WBQSPI_RESET 5'h0 |
`define WBQSPI_RESET_QUADMODE 5'h1 |
`define WBQSPI_IDLE 5'h2 |
`define WBQSPI_RDIDLE 5'h3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 5'h4 |
`define WBQSPI_RD_DUMMY 5'h5 |
`define WBQSPI_QRD_ADDRESS 5'h6 |
`define WBQSPI_QRD_DUMMY 5'h7 |
`define WBQSPI_READ_CMD 5'h8 |
`define WBQSPI_READ_DATA 5'h9 |
`define WBQSPI_WAIT_TIL_RDIDLE 5'h10 |
`define WBQSPI_READ_ID_CMD 5'h11 |
`define WBQSPI_READ_ID 5'h12 |
`define WBQSPI_READ_STATUS 5'h13 |
`define WBQSPI_READ_CONFIG 5'h14 |
`define WBQSPI_WAIT_TIL_IDLE 5'h15 |
`define WBQSPI_RESET 5'd0 |
`define WBQSPI_RESET_QUADMODE 5'd1 |
`define WBQSPI_IDLE 5'd2 |
`define WBQSPI_RDIDLE 5'd3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 5'd4 |
`define WBQSPI_RD_DUMMY 5'd5 |
`define WBQSPI_QRD_ADDRESS 5'd6 |
`define WBQSPI_QRD_DUMMY 5'd7 |
`define WBQSPI_READ_CMD 5'd8 |
`define WBQSPI_READ_DATA 5'd9 |
`define WBQSPI_WAIT_TIL_RDIDLE 5'd10 |
`define WBQSPI_READ_ID_CMD 5'd11 |
`define WBQSPI_READ_ID 5'd12 |
`define WBQSPI_READ_STATUS 5'd13 |
`define WBQSPI_READ_CONFIG 5'd14 |
`define WBQSPI_WAIT_TIL_IDLE 5'd15 |
// |
// |
`ifndef READ_ONLY |
// |
`define WBQSPI_WAIT_WIP_CLEAR 5'h16 |
`define WBQSPI_CHECK_WIP_CLEAR 5'h17 |
`define WBQSPI_CHECK_WIP_DONE 5'h18 |
`define WBQSPI_WEN 5'h19 |
`define WBQSPI_PP 5'h20 // Program page |
`define WBQSPI_QPP 5'h21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 5'h22 |
`define WBQSPI_WR_BUS_CYCLE 5'h23 |
`define WBQSPI_WRITE_STATUS 5'h24 |
`define WBQSPI_WRITE_CONFIG 5'h25 |
`define WBQSPI_ERASE_WEN 5'h26 |
`define WBQSPI_ERASE_CMD 5'h27 |
`define WBQSPI_ERASE_BLOCK 5'h28 |
`define WBQSPI_CLEAR_STATUS 5'h29 |
`define WBQSPI_IDLE_CHECK_WIP 5'h30 |
`define WBQSPI_WAIT_WIP_CLEAR 5'd16 |
`define WBQSPI_CHECK_WIP_CLEAR 5'd17 |
`define WBQSPI_CHECK_WIP_DONE 5'd18 |
`define WBQSPI_WEN 5'd19 |
`define WBQSPI_PP 5'd20 // Program page |
`define WBQSPI_QPP 5'd21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 5'd22 |
`define WBQSPI_WR_BUS_CYCLE 5'd23 |
`define WBQSPI_WRITE_STATUS 5'd24 |
`define WBQSPI_WRITE_CONFIG 5'd25 |
`define WBQSPI_ERASE_WEN 5'd26 |
`define WBQSPI_ERASE_CMD 5'd27 |
`define WBQSPI_ERASE_BLOCK 5'd28 |
`define WBQSPI_CLEAR_STATUS 5'd29 |
`define WBQSPI_IDLE_CHECK_WIP 5'd30 |
// |
`endif |
|
module wbqspiflash(i_clk_100mhz, |
module wbqspiflash(i_clk, |
// Internal wishbone connections |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we, |
i_wb_addr, i_wb_data, |
103,10 → 103,11
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
o_interrupt); |
parameter ADDRESS_WIDTH=22; |
input wire i_clk_100mhz; |
localparam AW = ADDRESS_WIDTH-2; |
input wire i_clk; |
// Wishbone, inputs first |
input wire i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input wire [(ADDRESS_WIDTH-3):0] i_wb_addr; |
input wire [(AW-1):0] i_wb_addr; |
input wire [31:0] i_wb_data; |
// then outputs |
output reg o_wb_ack; |
130,7 → 131,7
wire [3:0] w_qspi_dat; |
wire [1:0] w_qspi_mod; |
// wire [22:0] spi_dbg; |
llqspi lldriver(i_clk_100mhz, |
llqspi lldriver(i_clk, |
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir, |
spi_out, spi_valid, spi_busy, |
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat, |
166,13 → 167,15
endgenerate |
|
reg [7:0] last_status; |
reg [9:0] reset_counter; |
reg quad_mode_enabled; |
reg spif_cmd, spif_override; |
reg [31:0] spif_data; |
reg [4:0] state; |
reg spif_ctrl, spif_req; |
reg alt_cmd, alt_ctrl; |
wire [(ADDRESS_WIDTH-17):0] spif_sector; |
assign spif_sector = spif_addr[(ADDRESS_WIDTH-3):14]; |
assign spif_sector = spif_addr[(AW-1):14]; |
|
// assign o_debug = { spi_wr, spi_spd, spi_hold, state, spi_dbg }; |
|
183,9 → 186,13
initial spi_len = 2'b00; |
initial quad_mode_enabled = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk_100mhz) |
initial spif_override = 1'b1; |
initial spif_ctrl = 1'b0; |
always @(posedge i_clk) |
begin |
spif_override <= 1'b0; |
alt_cmd <= (reset_counter[9:8]==2'b10)?reset_counter[3]:1'b1; // Toggle CS_n |
alt_ctrl <= (reset_counter[9:8]==2'b10)?reset_counter[0]:1'b1; // Toggle clock too |
if (state == `WBQSPI_RESET) |
begin |
// From a reset, we should |
203,7 → 210,8
state <= `WBQSPI_RESET_QUADMODE; |
spif_req <= 1'b0; |
spif_override <= 1'b1; |
last_status <= 8'hfc; // |
last_status <= 8'h00; // |
reset_counter <= 10'h3fc; // |
// This guarantees that we aren't starting in quad |
// I/O mode, where the FPGA configuration scripts may |
// have left us. |
219,15 → 227,20
o_wb_stall <= 1'b1; |
|
// Do something ... |
if (last_status == 8'h00) |
if (reset_counter == 10'h00) |
begin |
spif_override <= 1'b0; |
state <= `WBQSPI_IDLE; |
|
// Find out if we can use Quad I/O mode ... |
state <= `WBQSPI_READ_CONFIG; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; |
spi_in <= { 8'h35, 24'h00}; |
|
end else begin |
last_status <= last_status - 8'h1; |
reset_counter <= reset_counter - 10'h1; |
spif_override <= 1'b1; |
spif_cmd <= last_status[3]; // Toggle CS_n |
spif_ctrl <= last_status[0]; // Toggle clock too |
end |
end else if (state == `WBQSPI_IDLE) |
begin |
311,10 → 324,10
o_wb_stall <= 1'b1; |
case(i_wb_addr[1:0]) |
2'b00: begin // Erase command register |
write_protect <= ~i_wb_data[28]; |
write_protect <= !i_wb_data[28]; |
o_wb_stall <= 1'b0; |
|
if((i_wb_data[31])&&(~write_in_progress)) |
if((i_wb_data[31])&&(!write_in_progress)) |
begin |
// Command an erase--ack it immediately |
|
368,7 → 381,7
end |
endcase |
`endif |
end else if (i_wb_ctrl_stb) // &&(~i_wb_we)) |
end else if (i_wb_ctrl_stb) // &&(!i_wb_we)) |
begin |
case(i_wb_addr[1:0]) |
2'b00: begin // Read local register |
444,7 → 457,7
spi_hold <= 1'b0; |
spi_spd<= 1'b1; |
spi_dir <= 1'b0; // Write (for now) |
if ((i_wb_data_stb)&&(~i_wb_we)) |
if ((i_wb_data_stb)&&(!i_wb_we)) |
begin // Continue our read ... send the new address / mode |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
451,7 → 464,7
spi_len <= 2'b10; // Write address, but not mode byte |
spi_in <= { w_wb_addr, 8'ha0 }; |
state <= `WBQSPI_QRD_DUMMY; |
end else if((i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
end else if((i_wb_ctrl_stb)&&(!i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
begin |
// A local read that doesn't touch the device, so leave |
// the device in its current state |
1030,7 → 1043,7
// 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])) |
&&(i_wb_addr[(AW-1):6]==spif_addr[(AW-1):6])) |
begin |
spif_cmd <= 1'b1; |
spif_data <= i_wb_data; |
1103,7 → 1116,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[(ADDRESS_WIDTH-3):14]; |
erased_sector <= spif_data[(AW-1):14]; |
// and marking this erase sector as no longer dirty |
dirty_sector <= 1'b0; |
|
1121,11 → 1134,10
o_wb_ack <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
// When the port clears, we can head back to idle |
// No ack necessary, we ackd before getting |
// here. |
if ((~spi_busy)&&(~spi_wr)) |
begin |
o_wb_ack <= spif_req; |
state <= `WBQSPI_IDLE; |
end |
end else if (state == `WBQSPI_CLEAR_STATUS) |
begin // Issue a clear status command |
spi_wr <= 1'b1; |
1185,8 → 1197,8
end |
|
// Command and control during the reset sequence |
assign o_qspi_cs_n = (spif_override)?spif_cmd :w_qspi_cs_n; |
assign o_qspi_sck = (spif_override)?spif_ctrl:w_qspi_sck; |
assign o_qspi_mod = (spif_override)? 2'b01 :w_qspi_mod; |
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat; |
assign o_qspi_cs_n = (spif_override)?alt_cmd :w_qspi_cs_n; |
assign o_qspi_sck = (spif_override)?alt_ctrl:w_qspi_sck; |
assign o_qspi_mod = (spif_override)? 2'b01 :w_qspi_mod; |
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat; |
endmodule |