该芯片是Micron公司的Serial NOR FLASH,主存储区为1Gbits,分为4个256Mb的die,基于SPI协议通信。支持3-byte和4-byte地址。支持4KB\64KB\32MB擦除。
本文主要实现了图中红框所示命令,以下所述是在EXTENDED SPI协议下
-对于任意写操作(写寄存器或者PROGRAM)都需要执行WRITE ENABLE命令.
其时序为:
发送8比特命令0x06,之后片选拉高
-工程中使用Write extended address register命令
其时序为:
发送16比特,其中前8比特为命令0xC5,后8比特为扩展地址
-读寄存器相对写寄存器少了写使能操作,工程中使用了读状态寄存器命令,读标志状态寄存器命令和读扩展地址命令。
READ STATUS REG/READ FLAG STATUS REG/READ EXTENDED ADDRESS REG时序
-使用了3-byte寻址模式,根据该芯片的主存区划分,3-byte地址只能访问128Mb数据量,但是该芯片支持一次READ操作读取1个die(即256Mb数据量),需要配置extended address register来访问其他3个die的存储空间。也就是说读取完该芯片所有容量1Gb至少需要4次读操作。
READ操作时序
-擦除之后,存储区数据为1,也就是说写操作将1变成0.每一次写操作最多能够写256byte(one page),同时注意写之前需确认该区域已经擦除。
PROGRAM MEMORY时序
-工程中需要一次性将整块颗粒擦除完,所以使用die擦除方式。
-擦除与read类似,如果要擦除整片,在3-byte寻址模式下,需要修改扩展地址。
-整片擦除操作步骤
-a)写使能
-b)写扩展地址addr_extend=0x00
-c)die erase命令0xC4
-d)轮询读取状态寄存器,直至bit0为0
-e)读取FLAG STATUS寄存器,同时判断是否擦除完整块芯片,如果没有:判断bit7若为1则跳转b)更改addr_extend+=2,否则跳转c);如果擦除完,擦除成功,退出擦除状态
module spi_flash_config
(
input rst_n,
input sys_clk,
input [31:0] send_data,
input send_trig,
input [15:0] conf_data,
output reg[15:0] rece_data,
output reg rece_data_valid,
output reg send_done,
output spi_sclk_o ,
output spi_ncs_o ,
output spi_mosi_o ,
input spi_miso_i
);
reg [2:0] spi_state;
parameter s_IDLE = 3’d0,
s_NCS = 3’d1,
s_CMD = 3’d2,
s_RECE_DATA = 3’d3,
s_DELAY = 3’d4,
s_END = 3’d5;
reg [7:0] cnt_delay;
reg [7:0] cnt_send ;
reg [7:0] cnt_sclk ;
reg[31:0] send_buff;
wire rece_en ;
reg [7:0] cnt_rece ;
reg [15:0] rece_buff;
reg spi_ncs ;
wire spi_mosi ;
wire spi_sclk ;
assign spi_sclk_o = spi_sclk;
assign spi_ncs_o = spi_ncs;
assign spi_mosi_o = spi_mosi;
//generate spi_ncs ,spi_sclk,spi_mosi
always@(posedge sys_clk)
begin
if(rst_n == 1’b0)
begin
spi_state <= 3’d0;
cnt_delay <= 8’h0;
cnt_send <= 8'h0;
cnt_rece <= 8'h0;
cnt_sclk <= 'h0;
send_buff <= 32'h0;
spi_ncs <= 1'b1;
send_done <= 1'b0;
end
else
begin
case(spi_state)
s_IDLE:
begin
if(send_trig)
begin
cnt_send <= conf_data[7:0];
cnt_rece <= conf_data[15:8];
send_buff <= send_data;
spi_state <= s_NCS;
end
else
begin
send_buff <= 32'h0;
spi_state <= s_IDLE;
end
spi_ncs <= 1'b1;
cnt_delay<= 8'd2;
send_done<= 1'b0;
end
s_NCS://1
begin
spi_ncs <= 1'b0;
if(cnt_delay == 8'd0)
spi_state <= s_CMD;
else
cnt_delay <= cnt_delay - 1'b1;
end
s_CMD://2
begin
send_buff <= {send_buff[30:0],1'b0};
if(cnt_send == 0)
begin
if(cnt_rece == 'h0)
spi_state <= s_DELAY;
else
spi_state <= s_RECE_DATA;
cnt_sclk <= 'h0;
end
else
cnt_send <= cnt_send - 1'b1;
end
s_RECE_DATA://3
begin
cnt_delay<= 8'd2;
if(cnt_sclk == cnt_rece)
begin
cnt_sclk <= 'h0;
spi_state <= s_DELAY;
end
else
cnt_sclk <= cnt_sclk + 1'b1;
end
s_DELAY://4
begin
if(cnt_delay == 8'd0)
begin
send_done <= 1'b1;
spi_state <= s_END;
spi_ncs <= 1'b1;
end
else
cnt_delay <= cnt_delay - 1'b1;
end
s_END://5
begin
send_done <= 1'b0;
spi_state <= s_IDLE;
spi_ncs <= 1'b1;
end
default:
begin
send_done <= 1'b0;
spi_state <= s_IDLE;
spi_ncs <= 1'b1;
end
endcase
end
end
assign spi_mosi = send_buff[31];
assign spi_sclk = ((spi_state == s_CMD) || (spi_state == s_RECE_DATA)) && (~sys_clk);
assign rece_en = (spi_state == s_RECE_DATA);
//receive data
initial begin rece_buff = ‘h0;end
always@(posedge spi_sclk or posedge send_trig)
begin
if(send_trig)
rece_buff <= ‘h0;
else
if(rece_en)
rece_buff <= {rece_buff[14:0],spi_miso_i};
else;
end
reg rece_en_r;
reg rece_en_rr;
reg rece_en_falling;
always@(posedge sys_clk)
begin
if(rst_n == 1’b0)
begin
rece_data <= ‘h0;
rece_data_valid <= 1’b0;
rece_en_r <= 1'b0;
rece_en_rr <= 1'b0;
rece_en_falling <= 1'b0;
end
else
begin
rece_en_r <= rece_en;
rece_en_rr <= rece_en_r;
rece_en_falling <= rece_en_rr &&(~rece_en_r);
if(rece_en_rr &&(~rece_en_r))
begin
rece_data <= rece_buff;
rece_data_valid <= 1'b1;
end
else
begin
rece_data <= rece_data;
rece_data_valid <= 1'b0;
end
end
end
endmodule