本篇文章所使用的Flash型号为M25P16,是ST公司的一款(好像还有一款同名的,是别的公司的)。容量为16Mbit,SPI接口,时钟速率可达50Mhz。要想完成对Flash的读写擦除操作,只需要弄懂两点即可:SPI时序 和 Flash操作指令。其他的细节和一些概念可以学习的过程中了解补充。
SPI一共有四种模式,分别由两个变量CPOL和CPHA控制。此Flash芯片下面两种模式
一般来说,都采用第二种模式对Flash进行读写。
Flash的全部指令如下,不算多,而且其中有些指令不会用到。下面就对必须要用到的指令进行说明
这两条指令一个是写使能,另外一个是写失能,一般情况下,只用WREN就可以看,至于为什么,后面相关写指令会给出答案。
用来读取Flash的设备ID号,通常用来测试SPI总线是否正确,写完SPI协议后,可以使用这条命令来进行测试。
发送指令和读取的首地址后,接下来就是读取数据,每读取一个数据,地址就会知道加一,当达到地址边界后,地址会自动跳转到0地址进行读取,读取数据的数量没有限制,可以无限读下去。读完最后一个字节后,只需要将S信号拉高即可结束本部读取操作。
对flash进行写入操作,以页为单位,每一页有256bytes,共8192页,具体如下。
然后时序图如下,
然后重点来了,意思就是,在执行PP命令的时候,需要先执行WREN命令,并且锁住,可能在PP命令结束后,WREN命令会失效,根据最后一行推测出来的,测试时确实如此。
擦除命令,很简单,给指令和地址后,就ok了,擦除完成后,再次读取的时候,应该是全FF。同样擦除命令和PP命令一样,需要先发送WREN命令才行。其他的就没了。
Tcsh : 表示两条指令间隔时间,
Tpp: 表示page program后,需要等待Tpp时间后,才能进行下一条指令的操作
Tse: 同Tpp一样。
写流程: WREN -> SE -> WREN -> PP(PP只能将1写为0 ,所以在PP之前要先擦除)
读流程: READ
SPI模块
`timescale 1ns/1ps
//MODE : CPOL=1 CPHA=1
//spi_clk = sys_clk / 2
module SPI_Master (
//system interface
input wire sys_clk,
input wire sys_rstn,
//user inferface
//user read
input wire read_req,
output wire[7:0] read_data,
output wire read_ack,
//user write
input wire write_req,
input wire[7:0] write_data,
output wire write_ack,
//spi to external flash
output wire spi_clk,
output wire spi_mosi,
input wire spi_miso
//output wire spi_csn
);
localparam SPI_IDLE = 4'b0001;
localparam SPI_DATA = 4'b0010;
localparam SPI_END = 4'b0100;
localparam SPI_END2 = 4'b1000;
reg[3:0] state , next_state;
reg spi_clk_reg;
reg spi_mosi_reg;
reg spi_csn_reg;
reg spi_clk_inverse_cnt;
reg[3:0] spi_rev_send_bit_cnt;
reg[7:0] write_data_reg;
reg[7:0] read_data_reg;
assign spi_clk = spi_clk_reg;
assign spi_mosi = spi_mosi_reg;
assign spi_csn = spi_csn_reg;
assign read_data = read_data_reg;
assign read_ack = (state == SPI_END) ? 1'b1 : 1'b0;
assign write_ack = (state == SPI_END) ? 1'b1 : 1'b0;
always @(posedge sys_clk or negedge sys_rstn) begin
if (sys_rstn == 1'b0)
state <= SPI_IDLE;
else
state <= next_state;
end
always@(*)begin
case (state)
SPI_IDLE:
if( write_req == 1'b1 || read_req == 1'b1)
next_state <= SPI_DATA;
else
next_state <= SPI_IDLE;
SPI_DATA:
if( spi_rev_send_bit_cnt == 'd7 && spi_clk_inverse_cnt == 1'b1)
next_state <= SPI_END;
else
next_state <= SPI_DATA;
SPI_END:
next_state <= SPI_END2;
SPI_END2:
next_state <= SPI_IDLE;
default: next_state <= SPI_IDLE;
endcase
end
always @(posedge sys_clk or negedge sys_rstn) begin
if( sys_rstn == 1'b0)
spi_clk_inverse_cnt <= 1'b0;
else if( state == SPI_DATA)
spi_clk_inverse_cnt <= spi_clk_inverse_cnt + 1'b1;
else
spi_clk_inverse_cnt <= 1'b0;
end
//spi csn out
// always @(posedge sys_clk or negedge sys_rstn) begin
// if( sys_rstn == 1'b0 )
// spi_csn_reg <= 1'b1;
// else if( state == SPI_IDLE && (write_req == 1'b1 || read_req == 1'b1))
// spi_csn_reg <= 1'b0;
// else if( state == SPI_DATA)
// spi_csn_reg <= 1'b0;
// else
// spi_csn_reg <= 1'b1;
// end
//spi write/read data bit cnt
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
spi_rev_send_bit_cnt <= 4'd0;
else if( spi_clk_inverse_cnt == 1'b1)
spi_rev_send_bit_cnt <= spi_rev_send_bit_cnt + 1'b1;
else if( state == SPI_DATA)
spi_rev_send_bit_cnt <= spi_rev_send_bit_cnt;
else
spi_rev_send_bit_cnt <= 4'd0;
end
//mosi data shift
always @(posedge sys_clk or negedge sys_rstn) begin
if( sys_rstn == 1'b0)
write_data_reg <= 8'd0;
else if( state == SPI_IDLE && (write_req == 1'b1 || read_req == 1'b1))
write_data_reg <= write_data;
else if( state == SPI_DATA && spi_clk_inverse_cnt == 1'b1)
write_data_reg <= {write_data_reg[6:0],write_data_reg[7]};
else
write_data_reg <= write_data_reg;
end
//spi_clk_gen
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
spi_clk_reg <= 1'b1;
else if(state == SPI_DATA)
spi_clk_reg <= ~spi_clk_reg;
else
spi_clk_reg <= 1'b1;
end
//mosi data out
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
spi_mosi_reg <= 1'b1;
else if(state == SPI_DATA && write_req == 1'b1)
spi_mosi_reg <= write_data_reg[7];
else
spi_mosi_reg <= 1'b1;
end
//miso data in
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
read_data_reg <= 1'b0;
else if(state == SPI_DATA && spi_clk_inverse_cnt == 1'b1)
read_data_reg <= {read_data_reg[6:0] , spi_miso};
else
read_data_reg <= read_data_reg;
end
endmodule
Flash模块
//flash write/read control : M25P16
module flash_contorl (
//system interface
input wire sys_clk,
input wire sys_rstn,
//user interface
//read identification
input wire read_id_req,
output wire[23:0] flash_id,
output wire read_id_end,
//read data
input wire read_req,
input wire[23:0] read_addr,
input wire[9:0] read_size,
output wire[7:0] read_data,
output wire read_ack,
output wire read_end,
//write enalbe
input wire write_enable_req,
output wire write_enable_end,
//write disalbe
//write data
input wire write_req,
input wire[23:0] write_page, //write num page
input wire[8:0] write_size, //max equal 256
input wire[7:0] write_data,
output wire write_ack,
output wire write_end,
//erase sector
input wire erase_sector_req,
input wire[23:0] erase_sector_addr,
output wire erase_sector_end,
//erase bulk
input wire erase_bulk_req,
output wire erase_bulk_end,
//spi to external flash
output wire spi_clk,
output wire spi_mosi,
input wire spi_miso,
output wire spi_csn
);
//control flash instruction
`define Write_Enable 8'h06
`define Write_Disable 8'h04
`define Read_Identification 8'h9F
`define Read_Status_Reg 8'h05
`define Write_Status_Reg 8'h01
`define Read_Data_Bytes 8'h03
`define Read_Data_At_Higher_Speed 8'h0B
`define Page_Program 8'h02
`define Sector_Erase 8'hD8
`define Bulk_Erase 8'hC7
`define Read_Identification_Bytes 4'd4
`define Sector_Erase_Bytes 4'd4
`define Bulk_Erase_Bytes 4'd1
localparam Page_Size = 9'd256;
localparam Write_Enable_Wait = 'd10;
localparam Read_Data_Bytes_Wait = 'd10; //wait 60ns
localparam Sector_Erase_Wait = 32'd35_000_000; //wait 640ms
localparam Page_Program_Wait = 32'd350_000; //wait 640us
localparam Bulk_Erase_Wait = 32'd650_000_000; //wait 13s
localparam Flash_Idle = 13'b0_0000_0000_0001;
localparam Flash_Write_Enable = 13'b0_0000_0000_0010;
localparam Flash_Write_Disable = 13'b0_0000_0000_0100;
localparam Flash_Read_Identification = 13'b0_0000_0000_1000;
localparam Flash_Read_Status_Reg = 13'b0_0000_0001_0000;
localparam Flash_Write_Status_Reg = 13'b0_0000_0010_0000;
localparam Flash_Read_Data_Bytes = 13'b0_0000_0100_0000;
localparam Flash_Read_Data_At_hSpeed = 13'b0_0000_1000_0000;
localparam Flash_Page_Program = 13'b0_0001_0000_0000;
localparam Flash_Sector_Erase = 13'b0_0010_0000_0000;
localparam Flash_Bulk_Erase = 13'b0_0100_0000_0000;
localparam Flash_Wait = 13'b0_1000_0000_0000;
localparam Flash_End = 13'b1_0000_0000_0000;
reg[12:0] state , next_state;
reg[12:0] state_ts;
//spi read
reg spi_read_req;
wire[7:0] spi_read_data;
wire spi_read_ack;
//spi write
reg spi_write_req;
reg[7:0] spi_write_data;
wire spi_write_ack;
reg spi_csn_reg;
reg[9:0] spi_wr_byte_cnt;
reg[23:0] flash_id_reg;
reg[32:0] pp_erase_wait_cnt;
assign spi_csn = spi_csn_reg; //to spi flash csn
assign flash_id = flash_id_reg;
assign read_id_end = ((spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0;
assign read_data = spi_read_data;
assign read_ack = ((state == Flash_Read_Data_Bytes) && (spi_wr_byte_cnt > 'd3) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0; //读出数据有效
assign read_end = ((state == Flash_Read_Data_Bytes) &&(spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0;
assign write_enable_end = ((state == Flash_Write_Enable) &&(spi_wr_byte_cnt == 'd0) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0;
assign write_ack = ((state == Flash_Page_Program) && (spi_wr_byte_cnt > 'd3) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0; //请求下一个数据
assign write_end = ((state == Flash_Page_Program) && (spi_wr_byte_cnt == write_size + 'd1 + 'd3 - 1'b1) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0;
assign erase_sector_end = ((state == Flash_Sector_Erase) && (spi_wr_byte_cnt == `Sector_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1 ) ? 1'b1 : 1'b0;
assign erase_bulk_end = ((state == Flash_Bulk_Erase) && (spi_wr_byte_cnt == `Bulk_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1 ) ? 1'b1 : 1'b0;
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
state <= Flash_Idle;
else
state <= next_state;
end
always@(*)begin
case (state)
Flash_Idle:
if( read_id_req == 1'b1)
next_state <= Flash_Read_Identification;
else if( write_enable_req == 1'b1)
next_state <= Flash_Write_Enable;
else if( write_req == 1'b1 )
next_state <= Flash_Page_Program;
else if( read_req == 1'b1 )
next_state <= Flash_Read_Data_Bytes;
else if( erase_sector_req == 1'b1)
next_state <= Flash_Sector_Erase;
else if( erase_bulk_req == 1'b1 )
next_state <= Flash_Bulk_Erase;
else
next_state <= Flash_Idle;
Flash_Read_Identification:
if( (spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1)
next_state <= Flash_End;
else
next_state <= Flash_Read_Identification;
Flash_Write_Enable:
if( spi_write_ack == 1'b1)
next_state <= Flash_Wait;
else
next_state <= Flash_Write_Enable;
Flash_Page_Program:
if( (spi_wr_byte_cnt == write_size + 'd1 + 'd3 - 1'b1) && spi_write_ack == 1'b1) //写入指令 + 地址 + 数据
next_state <= Flash_Wait;
else
next_state <= Flash_Page_Program;
Flash_Read_Data_Bytes:
if( (spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1) //写入指令 + 地址 + 数据
next_state <= Flash_Wait;
else
next_state <= Flash_Read_Data_Bytes;
Flash_Sector_Erase:
if( (spi_wr_byte_cnt == `Sector_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1)
next_state <= Flash_Wait;
else
next_state <= Flash_Sector_Erase;
Flash_Bulk_Erase:
if( (spi_wr_byte_cnt == `Bulk_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1)
next_state <= Flash_Wait;
else
next_state <= Flash_Bulk_Erase;
Flash_Wait:
if( state_ts == Flash_Page_Program && pp_erase_wait_cnt == Page_Program_Wait)
next_state <= Flash_End;
else if(state_ts == Flash_Sector_Erase && pp_erase_wait_cnt == Sector_Erase_Wait)
next_state <= Flash_End;
else if(state_ts == Flash_Bulk_Erase && pp_erase_wait_cnt == Bulk_Erase_Wait)
next_state <= Flash_End;
else if(state_ts == Flash_Read_Data_Bytes && pp_erase_wait_cnt == Read_Data_Bytes_Wait)
next_state <= Flash_End;
else if(state_ts == Flash_Write_Enable && pp_erase_wait_cnt == Write_Enable_Wait)
next_state <= Flash_End;
else
next_state <= Flash_Wait;
Flash_End:
next_state <= Flash_Idle;
default: next_state <= Flash_Idle;
endcase
end
always@(posedge sys_clk or negedge sys_rstn ) begin
if( sys_rstn == 1'b0)
state_ts <= Flash_Idle;
else if( state == Flash_Idle )
if( write_req == 1'b1 )
state_ts <= Flash_Page_Program;
else if( erase_sector_req == 1'b1)
state_ts <= Flash_Sector_Erase;
else if( erase_bulk_req == 1'b1 )
state_ts <= Flash_Bulk_Erase;
else if( read_req == 1'b1)
state_ts <= Flash_Read_Data_Bytes;
else if( write_enable_req == 1'b1)
state_ts <= Flash_Write_Enable;
else
state_ts <= Flash_Idle;
else
state_ts <= state_ts;
end
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
pp_erase_wait_cnt <= 'd0;
else if(state == Flash_Wait)
pp_erase_wait_cnt <= pp_erase_wait_cnt + 1'b1;
else
pp_erase_wait_cnt <= 'd0;
end
// spi csn
always@(posedge sys_clk or negedge sys_rstn )begin
if( sys_rstn == 1'b0)
spi_csn_reg <= 1'b1;
else if( state == Flash_Idle || state == Flash_End || state == Flash_Wait)
spi_csn_reg <= 1'b1;
else
spi_csn_reg <= 1'b0;
end
//spi read/write byte cnt
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0 )
spi_wr_byte_cnt <= 'd0;
else if( state != next_state)
spi_wr_byte_cnt <= 'd0;
else if( spi_read_ack == 1'b1 || spi_write_ack == 1'b1)
spi_wr_byte_cnt <= spi_wr_byte_cnt + 1'b1;
else
spi_wr_byte_cnt <= spi_wr_byte_cnt;
end
//spi read
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0 )
spi_read_req <= 1'b0;
else if( state == Flash_Read_Identification && spi_wr_byte_cnt > 'd0)
if((spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1)
spi_read_req <= 1'b0;
else
spi_read_req <= 1'b1;
else if(state == Flash_Read_Data_Bytes && spi_wr_byte_cnt > 'd3)
if((spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1)
spi_read_req <= 1'b0;
else
spi_read_req <= 1'b1;
else
spi_read_req <= 1'b0;
end
//read flash identification
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0)
flash_id_reg <= 'd0;
else if( state == Flash_Read_Identification && spi_wr_byte_cnt > 'd0)
if( spi_read_ack == 1'b1)
flash_id_reg <= {flash_id_reg[15:0],spi_read_data};
else
flash_id_reg <= flash_id_reg;
else
flash_id_reg <= flash_id_reg;
end
//spi write req
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0 )
spi_write_req <= 1'b0;
else if( state == Flash_Write_Enable)
spi_write_req <= 1'b1;
else if( state == Flash_Read_Identification && spi_wr_byte_cnt == 'd0)
spi_write_req <= 1'b1;
else if( state == Flash_Page_Program)
spi_write_req <= 1'b1;
else if( state == Flash_Read_Data_Bytes && spi_wr_byte_cnt < 'd4)
spi_write_req <= 1'b1;
else if( state == Flash_Sector_Erase && spi_wr_byte_cnt < 'd4 )
spi_write_req <= 1'b1;
else if( state == Flash_Bulk_Erase )
spi_write_req <= 1'b1;
else
spi_write_req <= 1'b0;
end
//spi write data
always@(posedge sys_clk or negedge sys_rstn)begin
if( sys_rstn == 1'b0 )
spi_write_data <= 8'd0;
else if( state == Flash_Write_Enable )
spi_write_data <= `Write_Enable;
else if( state == Flash_Read_Identification && spi_wr_byte_cnt == 'd0)
spi_write_data <= `Read_Identification;
else if( state == Flash_Page_Program)
case(spi_wr_byte_cnt)
'd0: spi_write_data <= `Page_Program;
'd1: spi_write_data <= write_page[23:16];
'd2: spi_write_data <= write_page[15:8];
'd3: spi_write_data <= write_page[7:0];
default: spi_write_data <= write_data;
endcase
else if(state == Flash_Read_Data_Bytes)
if( spi_wr_byte_cnt == 'd0)
spi_write_data <= `Read_Data_Bytes;
else if( spi_wr_byte_cnt == 'd1)
spi_write_data <= read_addr[23:16];
else if( spi_wr_byte_cnt == 'd2)
spi_write_data <= read_addr[15:8];
else
spi_write_data <= read_addr[7:0];
else if( state == Flash_Sector_Erase)
if( spi_wr_byte_cnt == 'd0)
spi_write_data <= `Sector_Erase;
else if( spi_wr_byte_cnt == 'd1)
spi_write_data <= erase_sector_addr[23:16];
else if( spi_wr_byte_cnt == 'd2)
spi_write_data <= erase_sector_addr[15:8];
else
spi_write_data <= erase_sector_addr[7:0];
else if( state == Flash_Bulk_Erase)
spi_write_data <= `Bulk_Erase;
else
spi_write_data <= 8'd0;
end
SPI_Master SPI_Master_hp(
//system interface
/*input wire sys_clk */ .sys_clk (sys_clk),
/*input wire sys_rstn*/ .sys_rstn (sys_rstn),
//user inferface
//user read
/*input wire read_req */ .read_req (spi_read_req),
/*output wire[7:0] read_data*/ .read_data (spi_read_data),
/* output wire read_ack*/ .read_ack (spi_read_ack),
//user write
/*input wire write_req */ .write_req (spi_write_req),
/*input wire[7:0] write_data*/ .write_data (spi_write_data),
/*output wire write_ack */ .write_ack (spi_write_ack),
//spi to external flash
/*output wire spi_clk */ .spi_clk (spi_clk),
/*output wire spi_mosi*/ .spi_mosi (spi_mosi),
/*input wire spi_miso*/ .spi_miso (spi_miso)
/*output wire spi_csn */ //.spi_csn ()
);
endmodule