Line 53... |
Line 53... |
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
//
|
//
|
`include "flash_config.v"
|
`include "flash_config.v"
|
`default_nettype none
|
`default_nettype none
|
//
|
//
|
`define WBQSPI_RESET 5'h0
|
`define WBQSPI_RESET 5'd0
|
`define WBQSPI_RESET_QUADMODE 5'h1
|
`define WBQSPI_RESET_QUADMODE 5'd1
|
`define WBQSPI_IDLE 5'h2
|
`define WBQSPI_IDLE 5'd2
|
`define WBQSPI_RDIDLE 5'h3 // Idle, but in fast read mode
|
`define WBQSPI_RDIDLE 5'd3 // Idle, but in fast read mode
|
`define WBQSPI_WBDECODE 5'h4
|
`define WBQSPI_WBDECODE 5'd4
|
`define WBQSPI_RD_DUMMY 5'h5
|
`define WBQSPI_RD_DUMMY 5'd5
|
`define WBQSPI_QRD_ADDRESS 5'h6
|
`define WBQSPI_QRD_ADDRESS 5'd6
|
`define WBQSPI_QRD_DUMMY 5'h7
|
`define WBQSPI_QRD_DUMMY 5'd7
|
`define WBQSPI_READ_CMD 5'h8
|
`define WBQSPI_READ_CMD 5'd8
|
`define WBQSPI_READ_DATA 5'h9
|
`define WBQSPI_READ_DATA 5'd9
|
`define WBQSPI_WAIT_TIL_RDIDLE 5'h10
|
`define WBQSPI_WAIT_TIL_RDIDLE 5'd10
|
`define WBQSPI_READ_ID_CMD 5'h11
|
`define WBQSPI_READ_ID_CMD 5'd11
|
`define WBQSPI_READ_ID 5'h12
|
`define WBQSPI_READ_ID 5'd12
|
`define WBQSPI_READ_STATUS 5'h13
|
`define WBQSPI_READ_STATUS 5'd13
|
`define WBQSPI_READ_CONFIG 5'h14
|
`define WBQSPI_READ_CONFIG 5'd14
|
`define WBQSPI_WAIT_TIL_IDLE 5'h15
|
`define WBQSPI_WAIT_TIL_IDLE 5'd15
|
//
|
//
|
//
|
//
|
`ifndef READ_ONLY
|
`ifndef READ_ONLY
|
//
|
//
|
`define WBQSPI_WAIT_WIP_CLEAR 5'h16
|
`define WBQSPI_WAIT_WIP_CLEAR 5'd16
|
`define WBQSPI_CHECK_WIP_CLEAR 5'h17
|
`define WBQSPI_CHECK_WIP_CLEAR 5'd17
|
`define WBQSPI_CHECK_WIP_DONE 5'h18
|
`define WBQSPI_CHECK_WIP_DONE 5'd18
|
`define WBQSPI_WEN 5'h19
|
`define WBQSPI_WEN 5'd19
|
`define WBQSPI_PP 5'h20 // Program page
|
`define WBQSPI_PP 5'd20 // Program page
|
`define WBQSPI_QPP 5'h21 // Program page, 4 bit mode
|
`define WBQSPI_QPP 5'd21 // Program page, 4 bit mode
|
`define WBQSPI_WR_DATA 5'h22
|
`define WBQSPI_WR_DATA 5'd22
|
`define WBQSPI_WR_BUS_CYCLE 5'h23
|
`define WBQSPI_WR_BUS_CYCLE 5'd23
|
`define WBQSPI_WRITE_STATUS 5'h24
|
`define WBQSPI_WRITE_STATUS 5'd24
|
`define WBQSPI_WRITE_CONFIG 5'h25
|
`define WBQSPI_WRITE_CONFIG 5'd25
|
`define WBQSPI_ERASE_WEN 5'h26
|
`define WBQSPI_ERASE_WEN 5'd26
|
`define WBQSPI_ERASE_CMD 5'h27
|
`define WBQSPI_ERASE_CMD 5'd27
|
`define WBQSPI_ERASE_BLOCK 5'h28
|
`define WBQSPI_ERASE_BLOCK 5'd28
|
`define WBQSPI_CLEAR_STATUS 5'h29
|
`define WBQSPI_CLEAR_STATUS 5'd29
|
`define WBQSPI_IDLE_CHECK_WIP 5'h30
|
`define WBQSPI_IDLE_CHECK_WIP 5'd30
|
//
|
//
|
`endif
|
`endif
|
|
|
module wbqspiflash(i_clk_100mhz,
|
module wbqspiflash(i_clk,
|
// Internal wishbone connections
|
// Internal wishbone connections
|
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we,
|
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we,
|
i_wb_addr, i_wb_data,
|
i_wb_addr, i_wb_data,
|
// Wishbone return values
|
// Wishbone return values
|
o_wb_ack, o_wb_stall, o_wb_data,
|
o_wb_ack, o_wb_stall, o_wb_data,
|
// Quad Spi connections to the external device
|
// Quad Spi connections to the external device
|
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat,
|
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat,
|
o_interrupt);
|
o_interrupt);
|
parameter ADDRESS_WIDTH=22;
|
parameter ADDRESS_WIDTH=22;
|
input wire i_clk_100mhz;
|
localparam AW = ADDRESS_WIDTH-2;
|
|
input wire i_clk;
|
// Wishbone, inputs first
|
// Wishbone, inputs first
|
input wire i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we;
|
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;
|
input wire [31:0] i_wb_data;
|
// then outputs
|
// then outputs
|
output reg o_wb_ack;
|
output reg o_wb_ack;
|
output reg o_wb_stall;
|
output reg o_wb_stall;
|
output reg [31:0] o_wb_data;
|
output reg [31:0] o_wb_data;
|
Line 128... |
Line 129... |
wire spi_valid, spi_busy;
|
wire spi_valid, spi_busy;
|
wire w_qspi_sck, w_qspi_cs_n;
|
wire w_qspi_sck, w_qspi_cs_n;
|
wire [3:0] w_qspi_dat;
|
wire [3:0] w_qspi_dat;
|
wire [1:0] w_qspi_mod;
|
wire [1:0] w_qspi_mod;
|
// wire [22:0] spi_dbg;
|
// 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_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir,
|
spi_out, spi_valid, spi_busy,
|
spi_out, spi_valid, spi_busy,
|
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat,
|
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat,
|
i_qspi_dat);
|
i_qspi_dat);
|
|
|
Line 164... |
Line 165... |
else
|
else
|
assign w_spif_addr = { {(24-ADDRESS_WIDTH){1'b0}}, spif_addr, 2'b00 };
|
assign w_spif_addr = { {(24-ADDRESS_WIDTH){1'b0}}, spif_addr, 2'b00 };
|
endgenerate
|
endgenerate
|
|
|
reg [7:0] last_status;
|
reg [7:0] last_status;
|
|
reg [9:0] reset_counter;
|
reg quad_mode_enabled;
|
reg quad_mode_enabled;
|
reg spif_cmd, spif_override;
|
reg spif_cmd, spif_override;
|
reg [31:0] spif_data;
|
reg [31:0] spif_data;
|
reg [4:0] state;
|
reg [4:0] state;
|
reg spif_ctrl, spif_req;
|
reg spif_ctrl, spif_req;
|
|
reg alt_cmd, alt_ctrl;
|
wire [(ADDRESS_WIDTH-17):0] spif_sector;
|
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 };
|
// assign o_debug = { spi_wr, spi_spd, spi_hold, state, spi_dbg };
|
|
|
initial state = `WBQSPI_RESET;
|
initial state = `WBQSPI_RESET;
|
initial o_wb_ack = 1'b0;
|
initial o_wb_ack = 1'b0;
|
initial o_wb_stall = 1'b1;
|
initial o_wb_stall = 1'b1;
|
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)
|
initial spif_override = 1'b1;
|
|
initial spif_ctrl = 1'b0;
|
|
always @(posedge i_clk)
|
begin
|
begin
|
spif_override <= 1'b0;
|
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)
|
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
|
Line 201... |
Line 208... |
spi_dir <= 1'b0;
|
spi_dir <= 1'b0;
|
last_status <= 8'h00;
|
last_status <= 8'h00;
|
state <= `WBQSPI_RESET_QUADMODE;
|
state <= `WBQSPI_RESET_QUADMODE;
|
spif_req <= 1'b0;
|
spif_req <= 1'b0;
|
spif_override <= 1'b1;
|
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
|
// This guarantees that we aren't starting in quad
|
// I/O mode, where the FPGA configuration scripts may
|
// I/O mode, where the FPGA configuration scripts may
|
// have left us.
|
// have left us.
|
end else if (state == `WBQSPI_RESET_QUADMODE)
|
end else if (state == `WBQSPI_RESET_QUADMODE)
|
begin
|
begin
|
Line 217... |
Line 225... |
// Not ready to handle the bus yet, so stall any requests
|
// Not ready to handle the bus yet, so stall any requests
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
|
|
// Do something ...
|
// Do something ...
|
if (last_status == 8'h00)
|
if (reset_counter == 10'h00)
|
begin
|
begin
|
spif_override <= 1'b0;
|
spif_override <= 1'b0;
|
state <= `WBQSPI_IDLE;
|
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
|
end else begin
|
last_status <= last_status - 8'h1;
|
reset_counter <= reset_counter - 10'h1;
|
spif_override <= 1'b1;
|
spif_override <= 1'b1;
|
spif_cmd <= last_status[3]; // Toggle CS_n
|
|
spif_ctrl <= last_status[0]; // Toggle clock too
|
|
end
|
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;
|
Line 309... |
Line 322... |
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
`else
|
`else
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
case(i_wb_addr[1:0])
|
case(i_wb_addr[1:0])
|
2'b00: begin // Erase command register
|
2'b00: begin // Erase command register
|
write_protect <= ~i_wb_data[28];
|
write_protect <= !i_wb_data[28];
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
|
|
if((i_wb_data[31])&&(~write_in_progress))
|
if((i_wb_data[31])&&(!write_in_progress))
|
begin
|
begin
|
// Command an erase--ack it immediately
|
// Command an erase--ack it immediately
|
|
|
o_wb_ack <= 1'b1;
|
o_wb_ack <= 1'b1;
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
Line 366... |
Line 379... |
o_wb_ack <= 1'b1;
|
o_wb_ack <= 1'b1;
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
end
|
end
|
endcase
|
endcase
|
`endif
|
`endif
|
end else if (i_wb_ctrl_stb) // &&(~i_wb_we))
|
end else if (i_wb_ctrl_stb) // &&(!i_wb_we))
|
begin
|
begin
|
case(i_wb_addr[1:0])
|
case(i_wb_addr[1:0])
|
2'b00: begin // Read local register
|
2'b00: begin // Read local register
|
if (write_in_progress) // Read status
|
if (write_in_progress) // Read status
|
begin// register, is write still in progress?
|
begin// register, is write still in progress?
|
Line 442... |
Line 455... |
spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb);
|
spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb);
|
spif_req <= (i_wb_ctrl_stb)||(i_wb_data_stb);
|
spif_req <= (i_wb_ctrl_stb)||(i_wb_data_stb);
|
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
spi_spd<= 1'b1;
|
spi_spd<= 1'b1;
|
spi_dir <= 1'b0; // Write (for now)
|
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
|
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; // Write address, but not mode byte
|
spi_len <= 2'b10; // Write address, but not mode byte
|
spi_in <= { w_wb_addr, 8'ha0 };
|
spi_in <= { w_wb_addr, 8'ha0 };
|
state <= `WBQSPI_QRD_DUMMY;
|
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
|
begin
|
// A local read that doesn't touch the device, so leave
|
// A local read that doesn't touch the device, so leave
|
// the device in its current state
|
// the device in its current state
|
o_wb_stall <= 1'b0;
|
o_wb_stall <= 1'b0;
|
o_wb_ack <= 1'b1;
|
o_wb_ack <= 1'b1;
|
Line 1028... |
Line 1041... |
end else if (spi_wr)
|
end else if (spi_wr)
|
begin // Give the SPI a chance to get busy on the last write
|
begin // Give the SPI a chance to get busy on the last write
|
// Do nothing here.
|
// Do nothing here.
|
end else if ((i_wb_data_stb)&&(i_wb_we)
|
end else if ((i_wb_data_stb)&&(i_wb_we)
|
&&(i_wb_addr == (spif_addr+1))
|
&&(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
|
begin
|
spif_cmd <= 1'b1;
|
spif_cmd <= 1'b1;
|
spif_data <= i_wb_data;
|
spif_data <= i_wb_data;
|
spif_addr <= i_wb_addr;
|
spif_addr <= i_wb_addr;
|
spif_ctrl <= 1'b0;
|
spif_ctrl <= 1'b0;
|
Line 1101... |
Line 1114... |
spi_in <= { 8'hd8, 2'h0, spif_data[19:14], 14'h000, 2'b00 };
|
spi_in <= { 8'hd8, 2'h0, spif_data[19:14], 14'h000, 2'b00 };
|
spi_len <= 2'b11; // 32 bit write
|
spi_len <= 2'b11; // 32 bit write
|
// together with setting our copy of the WIP bit
|
// together with setting our copy of the WIP bit
|
write_in_progress <= 1'b1;
|
write_in_progress <= 1'b1;
|
// keeping track of which sector we just erased
|
// 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
|
// and marking this erase sector as no longer dirty
|
dirty_sector <= 1'b0;
|
dirty_sector <= 1'b0;
|
|
|
// Wait for a full stop before issuing this command
|
// Wait for a full stop before issuing this command
|
if ((~spi_busy)&&(~spi_wr)&&(o_qspi_cs_n))
|
if ((~spi_busy)&&(~spi_wr)&&(o_qspi_cs_n))
|
Line 1119... |
Line 1132... |
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
o_wb_stall <= 1'b1;
|
o_wb_stall <= 1'b1;
|
o_wb_ack <= 1'b0;
|
o_wb_ack <= 1'b0;
|
spif_req <= (spif_req) && (i_wb_cyc);
|
spif_req <= (spif_req) && (i_wb_cyc);
|
// When the port clears, we can head back to idle
|
// When the port clears, we can head back to idle
|
|
// No ack necessary, we ackd before getting
|
|
// here.
|
if ((~spi_busy)&&(~spi_wr))
|
if ((~spi_busy)&&(~spi_wr))
|
begin
|
|
o_wb_ack <= spif_req;
|
|
state <= `WBQSPI_IDLE;
|
state <= `WBQSPI_IDLE;
|
end
|
|
end else if (state == `WBQSPI_CLEAR_STATUS)
|
end else if (state == `WBQSPI_CLEAR_STATUS)
|
begin // Issue a clear status command
|
begin // Issue a clear status command
|
spi_wr <= 1'b1;
|
spi_wr <= 1'b1;
|
spi_hold <= 1'b0;
|
spi_hold <= 1'b0;
|
spi_len <= 2'b00; // 8 bit command
|
spi_len <= 2'b00; // 8 bit command
|
Line 1183... |
Line 1195... |
end
|
end
|
end
|
end
|
end
|
end
|
|
|
// Command and control during the reset sequence
|
// Command and control during the reset sequence
|
assign o_qspi_cs_n = (spif_override)?spif_cmd :w_qspi_cs_n;
|
assign o_qspi_cs_n = (spif_override)?alt_cmd :w_qspi_cs_n;
|
assign o_qspi_sck = (spif_override)?spif_ctrl:w_qspi_sck;
|
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_mod = (spif_override)? 2'b01 :w_qspi_mod;
|
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat;
|
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
|