Line 50... |
Line 50... |
// http://www.gnu.org/licenses/gpl.html
|
// http://www.gnu.org/licenses/gpl.html
|
//
|
//
|
//
|
//
|
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
`define WBQSPI_RESET 0
|
`define WBQSPI_RESET 0
|
`define WBQSPI_RESET_WEN 1
|
`define WBQSPI_RESET_QUADMODE 1
|
`define WBQSPI_IDLE 2
|
`define WBQSPI_IDLE 2
|
`define WBQSPI_RDIDLE 3 // Idle, but in fast read mode
|
`define WBQSPI_RDIDLE 3 // Idle, but in fast read mode
|
`define WBQSPI_WBDECODE 4
|
`define WBQSPI_WBDECODE 4
|
`define WBQSPI_WAIT_WIP_CLEAR 5
|
`define WBQSPI_WAIT_WIP_CLEAR 5
|
`define WBQSPI_CHECK_WIP_CLEAR 6
|
`define WBQSPI_CHECK_WIP_CLEAR 6
|
Line 112... |
Line 112... |
reg spi_wr, spi_hold, spi_spd, spi_dir;
|
reg spi_wr, spi_hold, spi_spd, spi_dir;
|
reg [31:0] spi_in;
|
reg [31:0] spi_in;
|
reg [1:0] spi_len;
|
reg [1:0] spi_len;
|
wire [31:0] spi_out;
|
wire [31:0] spi_out;
|
wire spi_valid, spi_busy;
|
wire spi_valid, spi_busy;
|
|
wire w_qspi_sck, w_qspi_cs_n;
|
|
wire [3:0] w_qspi_dat;
|
|
wire [1:0] w_qspi_mod;
|
llqspi lldriver(i_clk_100mhz,
|
llqspi lldriver(i_clk_100mhz,
|
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir,
|
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir,
|
spi_out, spi_valid, spi_busy,
|
spi_out, spi_valid, spi_busy,
|
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat,
|
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat,
|
i_qspi_dat);
|
i_qspi_dat);
|
|
|
// Erase status tracking
|
// Erase status tracking
|
reg write_in_progress, write_protect;
|
reg write_in_progress, write_protect;
|
reg [5:0] erased_sector;
|
reg [5:0] erased_sector;
|
Line 131... |
Line 134... |
write_protect = 1'b1;
|
write_protect = 1'b1;
|
end
|
end
|
|
|
reg [7:0] last_status;
|
reg [7:0] last_status;
|
reg quad_mode_enabled;
|
reg quad_mode_enabled;
|
reg spif_cmd;
|
reg spif_cmd, spif_override;
|
reg [19:0] spif_addr;
|
reg [19:0] spif_addr;
|
reg [31:0] spif_data;
|
reg [31:0] spif_data;
|
reg [5:0] state;
|
reg [5:0] state;
|
reg [5:0] spif_return;
|
|
reg spif_ctrl, spif_req;
|
reg spif_ctrl, spif_req;
|
wire [5:0] spif_sector;
|
wire [5:0] spif_sector;
|
assign spif_sector = spif_addr[19:14];
|
assign spif_sector = spif_addr[19:14];
|
|
|
initial state = `WBQSPI_RESET;
|
initial state = `WBQSPI_RESET;
|
Line 148... |
Line 150... |
initial spi_wr = 1'b0;
|
initial spi_wr = 1'b0;
|
initial spi_len = 2'b00;
|
initial spi_len = 2'b00;
|
initial quad_mode_enabled = 1'b0;
|
initial quad_mode_enabled = 1'b0;
|
initial o_interrupt = 1'b0;
|
initial o_interrupt = 1'b0;
|
always @(posedge i_clk_100mhz)
|
always @(posedge i_clk_100mhz)
|
|
begin
|
|
spif_override <= 1'b0;
|
if (state == `WBQSPI_RESET)
|
if (state == `WBQSPI_RESET)
|
begin
|
begin
|
// From a reset, we should
|
// From a reset, we should
|
// Enable the Quad I/O mode
|
// Enable the Quad I/O mode
|
// Disable the Write protection bits in the status register
|
// Disable the Write protection bits in the status register
|
// Chip should already be up and running, so we can start
|
// Chip should already be up and running, so we can start
|
// immediately ....
|
// immediately ....
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
|
spi_wr <= 1'b0;
|
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
spi_spd <= 1'b0;
|
spi_spd <= 1'b0;
|
spi_dir <= 1'b0;
|
spi_dir <= 1'b0;
|
last_status <= 8'h00;
|
last_status <= 8'h00;
|
if (spi_len < 2'b11)
|
state <= `WBQSPI_RESET_QUADMODE;
|
spi_len <= spi_len + 1;
|
|
state <= `WBQSPI_IDLE;
|
|
spif_req <= 1'b0;
|
spif_req <= 1'b0;
|
/*
|
spif_override <= 1'b1;
|
// This next bit of logic is confusing ...
|
last_status <= 8'hfc; //
|
// I wish to enable quad SPI mode automatically. However, this is
|
// This guarantees that we aren't starting in quad
|
// a configuration register thing, so once written it never needs to
|
// I/O mode, where the FPGA configuration scripts may
|
// be adjusted again. Further, some bits within the configuration
|
// have left us.
|
// register cannot be undone, and my inkling might be to set those.
|
end else if (state == `WBQSPI_RESET_QUADMODE)
|
// Finally, the configuration register affects the control of the
|
begin
|
// block protect registers in the status register. So ...
|
// Okay, so here's the problem: we don't know whether or not
|
// let's just leave these registers alone, or command them from the
|
// the Xilinx loader started us up in Quad Read I/O idle mode.
|
// UART-wishbone interface.
|
// So, thus we need to
|
else if (~spi_busy)
|
// Not ready to handle the bus yet, so stall any requests
|
begin
|
o_wb_ack <= 1'b0;
|
spi_wr <= 1'b1;
|
o_wb_stall <= 1'b1;
|
spi_len <= 2'b00; // 1 byte
|
|
spi_in <= { 8'h06, 24'h000 };
|
// Do something ...
|
end
|
if (last_status == 8'h00)
|
end else if (state == `WBQSPI_RESET_WEN)
|
begin
|
begin
|
spif_override <= 1'b0;
|
spi_wr <= 1'b0;
|
state <= `WBQSPI_IDLE;
|
if ((~spi_busy)&&(~spi_wr)&&(o_qspi_cs_n))
|
end else begin
|
begin
|
last_status <= last_status - 8'h1;
|
spi_wr <= 1'b1;
|
spif_override <= 1'b1;
|
spi_spd <= 1'b0;
|
spif_cmd <= last_status[3]; // Toggle CS_n
|
spi_len <= 2'b10; // 3 bytes
|
spif_ctrl <= last_status[0]; // Toggle clock too
|
// Command, status, config, then don't care
|
end
|
spi_in <= { 8'h01, 8'h00, 8'h0b, 8'h00 };
|
|
spi_dir <= 1'b0;
|
|
quad_mode_enabled <= 1'b1;
|
|
state <= `WBQSPI_WAIT_TIL_IDLE;
|
|
write_in_progress <= 1'b1;
|
|
end
|
|
*/
|
|
end else if (state == `WBQSPI_IDLE)
|
end else if (state == `WBQSPI_IDLE)
|
begin
|
begin
|
o_interrupt <= 1'b0;
|
o_interrupt <= 1'b0;
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
Line 401... |
Line 397... |
spi_dir <= 1'b0; // Write (for now)
|
spi_dir <= 1'b0; // Write (for now)
|
if ((i_wb_cyc)&&(i_wb_data_stb)&&(~i_wb_we))
|
if ((i_wb_cyc)&&(i_wb_data_stb)&&(~i_wb_we))
|
begin // Continue our read ... send the new address / mode
|
begin // Continue our read ... send the new address / mode
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
spi_wr <= 1'b1;
|
spi_wr <= 1'b1;
|
spi_len <= 2'b10;
|
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 <= { 2'h0, i_wb_addr[19:0], 2'h0, 8'ha0 };
|
state <= `WBQSPI_QRD_DUMMY;
|
state <= `WBQSPI_QRD_DUMMY;
|
end else if((i_wb_cyc)&&(i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00))
|
end else if((i_wb_cyc)&&(i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00))
|
begin
|
begin
|
// A local read that doesn't touch the device, so leave
|
// A local read that doesn't touch the device, so leave
|
Line 629... |
Line 625... |
4'b00??: begin // Read data from ... somewhere
|
4'b00??: begin // Read data from ... somewhere
|
spi_wr <= 1'b1; // Write cmd to device
|
spi_wr <= 1'b1; // Write cmd to device
|
if (quad_mode_enabled)
|
if (quad_mode_enabled)
|
begin
|
begin
|
spi_in <= { 8'heb, 2'b00, spif_addr[19:0], 2'b00 };
|
spi_in <= { 8'heb, 2'b00, spif_addr[19:0], 2'b00 };
|
state <= `WBQSPI_QRD_DUMMY;
|
state <= `WBQSPI_QRD_ADDRESS;
|
|
// spi_len <= 2'b00; // single byte, cmd only
|
end else begin
|
end else begin
|
spi_in <= { 8'h0b, 2'b00, spif_addr[19:0], 2'b00 };
|
spi_in <= { 8'h0b, 2'b00, spif_addr[19:0], 2'b00 };
|
state <= `WBQSPI_RD_DUMMY;
|
state <= `WBQSPI_RD_DUMMY;
|
|
spi_len <= 2'b11; // Send cmd and addr
|
end end
|
end end
|
4'b10??: begin // Write data to ... anywhere
|
4'b10??: begin // Write data to ... anywhere
|
spi_wr <= 1'b1;
|
spi_wr <= 1'b1;
|
spi_len <= 2'b00; // 8 bits
|
spi_len <= 2'b00; // 8 bits
|
// Send a write enable command
|
// Send a write enable command
|
Line 672... |
Line 670... |
spi_wr <= 1'b0;
|
spi_wr <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
spif_req<= (spif_req) && (i_wb_cyc);
|
spif_req<= (spif_req) && (i_wb_cyc);
|
if ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)) // Let's come to a full stop
|
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 <= (quad_mode_enabled)?`WBQSPI_QPP:`WBQSPI_PP;
|
state <= `WBQSPI_PP;
|
// state <= `WBQSPI_PP;
|
end else if (state == `WBQSPI_PP)
|
end else if (state == `WBQSPI_PP)
|
begin // We come here under a full stop / full port idle mode
|
begin // We come here under a full stop / full port idle mode
|
// Issue our command immediately
|
// Issue our command immediately
|
spi_wr <= 1'b1;
|
spi_wr <= 1'b1;
|
spi_in <= { 8'h02, 2'h0, spif_addr, 2'b00 };
|
spi_in <= { 8'h02, 2'h0, spif_addr, 2'b00 };
|
Line 792... |
Line 790... |
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
|
|
spi_wr <= 1'b1; // Non-stop
|
spi_wr <= 1'b1; // Non-stop
|
spi_in <= { 2'b0, spif_addr, 2'b0, 8'ha0 };
|
spi_in <= { 2'b0, spif_addr, 2'b0, 8'ha0 };
|
spi_len <= 2'b11; // Write all 32 bits bits
|
spi_len <= 2'b10; // Write address, not mode byte
|
spi_spd <= 1'b1;
|
spi_spd <= 1'b1;
|
spi_dir <= 1'b0; // Still writing
|
spi_dir <= 1'b0; // Still writing
|
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
spif_req<= (spif_req) && (i_wb_cyc);
|
spif_req<= (spif_req) && (i_wb_cyc);
|
|
|
Line 834... |
Line 832... |
state <= `WBQSPI_READ_DATA;
|
state <= `WBQSPI_READ_DATA;
|
end else if (state == `WBQSPI_READ_DATA)
|
end else if (state == `WBQSPI_READ_DATA)
|
begin
|
begin
|
// Pipelined read support
|
// Pipelined read support
|
spi_wr <=((i_wb_cyc)&&(i_wb_data_stb)&&(~i_wb_we)&&(i_wb_addr== (spif_addr+1)));
|
spi_wr <=((i_wb_cyc)&&(i_wb_data_stb)&&(~i_wb_we)&&(i_wb_addr== (spif_addr+1)));
|
// spi_wr <= 1'b0; // No pipelined read support
|
|
spi_in <= 32'h00;
|
spi_in <= 32'h00;
|
spi_len <= 2'b11;
|
spi_len <= 2'b11;
|
// Don't adjust the speed here, it was set in the setup
|
// Don't adjust the speed here, it was set in the setup
|
spi_dir <= 1'b1; // Now we get to read
|
spi_dir <= 1'b1; // Now we get to read
|
spi_hold <= 1'b0;
|
// Don't let the device go to idle until the bus cycle ends.
|
|
// This actually prevents a *really* nasty race condition,
|
|
// where the strobe comes in after the lower level device
|
|
// has decided to stop waiting. The write is then issued,
|
|
// but no one is listening. By leaving the device open,
|
|
// the device is kept in a state where a valid strobe
|
|
// here will be useful. Of course, we don't accept
|
|
// all commands, just reads. Further, the strobe needs
|
|
// to be high for two clocks cycles without changing
|
|
// anything on the bus--one for us to notice it and pull
|
|
// our head out of the sand, and a second for whoever
|
|
// owns the bus to realize their command went through.
|
|
spi_hold <= 1'b1;
|
spif_req<= (spif_req) && (i_wb_cyc);
|
spif_req<= (spif_req) && (i_wb_cyc);
|
if ((spi_valid)&&(~spi_in[31]))
|
if ((spi_valid)&&(~spi_in[31]))
|
begin // Single pulse acknowledge and write data out
|
begin // Single pulse acknowledge and write data out
|
o_wb_ack <= spif_req;
|
o_wb_ack <= spif_req;
|
o_wb_stall <= (~spi_wr);
|
o_wb_stall <= (~spi_wr);
|
Line 851... |
Line 860... |
o_wb_data <= { spi_out[7:0], spi_out[15:8],
|
o_wb_data <= { spi_out[7:0], spi_out[15:8],
|
spi_out[23:16], spi_out[31:24] };
|
spi_out[23:16], spi_out[31:24] };
|
state <= (spi_wr)?`WBQSPI_READ_DATA
|
state <= (spi_wr)?`WBQSPI_READ_DATA
|
: ((spi_spd) ? `WBQSPI_WAIT_TIL_RDIDLE : `WBQSPI_WAIT_TIL_IDLE);
|
: ((spi_spd) ? `WBQSPI_WAIT_TIL_RDIDLE : `WBQSPI_WAIT_TIL_IDLE);
|
spif_req <= spi_wr;
|
spif_req <= spi_wr;
|
|
spi_hold <= (~spi_wr);
|
if (spi_wr)
|
if (spi_wr)
|
spif_addr <= i_wb_addr;
|
spif_addr <= i_wb_addr;
|
|
end else if (~i_wb_cyc)
|
|
begin // FAIL SAFE: If the bus cycle ends, forget why we're
|
|
// here, just go back to idle
|
|
state <= ((spi_spd) ? `WBQSPI_WAIT_TIL_RDIDLE : `WBQSPI_WAIT_TIL_IDLE);
|
|
spi_hold <= 1'b0;
|
|
o_wb_ack <= 1'b0;
|
|
o_wb_stall <= 1'b1;
|
end else begin
|
end else begin
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
end
|
end
|
end else if (state == `WBQSPI_WAIT_TIL_RDIDLE)
|
end else if (state == `WBQSPI_WAIT_TIL_RDIDLE)
|
Line 893... |
Line 910... |
end else if (state == `WBQSPI_READ_ID)
|
end else if (state == `WBQSPI_READ_ID)
|
begin
|
begin
|
o_wb_ack <= 1'b0; // Assuming we're still waiting
|
o_wb_ack <= 1'b0; // Assuming we're still waiting
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
|
|
spi_wr <= 1'b0; // No more writes, we'ev already written the cmd
|
spi_wr <= 1'b0; // No more writes, we've already written the cmd
|
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
spif_req <= (spif_req) && (i_wb_cyc);
|
spif_req <= (spif_req) && (i_wb_cyc);
|
|
|
// Here, we just wait until the result comes back
|
// Here, we just wait until the result comes back
|
// The problem is, the result may be the previous result.
|
// The problem is, the result may be the previous result.
|
Line 906... |
Line 923... |
if((spi_valid)&&(spi_len==2'b00))
|
if((spi_valid)&&(spi_len==2'b00))
|
begin // Put the results out as soon as possible
|
begin // Put the results out as soon as possible
|
o_wb_data <= spi_out[31:0];
|
o_wb_data <= spi_out[31:0];
|
o_wb_ack <= spif_req;
|
o_wb_ack <= spif_req;
|
spif_req <= 1'b0;
|
spif_req <= 1'b0;
|
end else if ((~spi_busy)&&(~o_qspi_cs_n))
|
end else if ((~spi_busy)&&(o_qspi_cs_n))
|
begin
|
begin
|
state <= `WBQSPI_IDLE;
|
state <= `WBQSPI_IDLE;
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
end
|
end
|
end else if (state == `WBQSPI_READ_STATUS)
|
end else if (state == `WBQSPI_READ_STATUS)
|
Line 1095... |
Line 1112... |
state <= `WBQSPI_IDLE;
|
state <= `WBQSPI_IDLE;
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
o_wb_ack <= 1'b0; // Shouldn't be acking anything here
|
o_wb_ack <= 1'b0; // Shouldn't be acking anything here
|
end
|
end
|
end
|
end
|
|
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;
|
endmodule
|
endmodule
|
|
|
No newline at end of file
|
No newline at end of file
|