1. Introduction
The Flash model used in this article is M25P16, which is a model of ST Company (there seems to be another model with the same name, which is from another company). The capacity is 16Mbit, SPI interface, the clock rate can reach 50Mhz. To complete the read, write and erase operations on Flash, you only need to understand two points: SPI timing and Flash operation instructions. Additional details and some concepts can be supplemented during the learning process.
2. SPI
SPI has four modes in total, controlled by two variables CPOL and CPHA respectively. The following two modes of this Flash chip
- The first one is all 0, the clock is low when the clock is idle, the data is output on the falling edge of the clock, and input on the rising edge. What needs to be noted in this mode is that the output of the first bit has no falling edge, and it has to be output manually in advance.
- The second is all 1, the clock is high when the clock is idle, the data is output on the falling edge of the clock, and input on the rising edge.
Generally speaking, the second mode is used to read and write Flash.
3. Flash command
All the instructions of Flash are as follows, not too many, and some of them will not be used. The instructions that must be used are described below
(1). WREN 和 WRDI
One of these two instructions is to enable writing, and the other is to disable writing. In general, only WREN can be used to read. As for why, the related writing instructions will give the answer later.
(2) RDIDs
The device ID number used to read Flash is usually used to test whether the SPI bus is correct. After writing the SPI protocol, you can use this command to test.
(3) READ
After sending the instruction and the first address to read, the next step is to read the data. Every time a data is read, the address will be incremented by one. When the address boundary is reached, the address will automatically jump to address 0 for reading. The amount of data is not limited and can be read indefinitely. After reading the last byte, you only need to pull the S signal high to end the reading operation of this part.
(4) PP
Write operation to flash, in page unit, each page has 256bytes, a total of 8192 pages, the details are as follows.
Then the timing diagram is as follows,
Then comes the important point, which means that when executing the PP command, you need to execute the WREN command first and lock it. Maybe after the PP command ends, the WREN command will become invalid. According to the last line , it is indeed true during the test.
(5) SE
The erase command is very simple. After the instruction and address are given, it will be ok. After the erase is completed, when it is read again, it should be all FF. Similarly, the erase command is the same as the PP command, and the WREN command needs to be sent first. The rest is gone.
4. Other matters needing attention
Tcsh: Indicates the time interval between two instructions,
Tpp: After the page program, you need to wait for the Tpp time before proceeding to the next instruction
Tse: Same as Tpp.
Five. Verilog implementation
Write process: WREN -> SE -> WREN -> PP (PP can only write 1 to 0, so it must be erased before PP)
Read process: READ
SPI module
`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 module
//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
The value of the test read ID is given below.
For the complete project, you can pay attention to reply FPGA read and write Flash acquisition