FPGA实现Flash读写操作
创始人
2025-05-30 01:52:51
0

一. 简介

本篇文章所使用的Flash型号为M25P16,是ST公司的一款(好像还有一款同名的,是别的公司的)。容量为16Mbit,SPI接口,时钟速率可达50Mhz。要想完成对Flash的读写擦除操作,只需要弄懂两点即可:SPI时序 和 Flash操作指令。其他的细节和一些概念可以学习的过程中了解补充。

二. SPI

SPI一共有四种模式,分别由两个变量CPOL和CPHA控制。此Flash芯片下面两种模式

  1. 第一种是都为0,时钟空闲时是低电平,数据在时钟的下降沿输出,在上升沿输入。这种模式需要注意的是,第一个bit的输出是没有下降沿的,得手动提前输出。
  2. 第二种是都为1,时钟空闲时是高电平,数据在时钟的下降沿输出,在上升沿输入。

一般来说,都采用第二种模式对Flash进行读写。

请添加图片描述

三. Flash指令

Flash的全部指令如下,不算多,而且其中有些指令不会用到。下面就对必须要用到的指令进行说明

请添加图片描述

(1). WREN 和 WRDI

这两条指令一个是写使能,另外一个是写失能,一般情况下,只用WREN就可以看,至于为什么,后面相关写指令会给出答案。
请添加图片描述

(2) RDID

用来读取Flash的设备ID号,通常用来测试SPI总线是否正确,写完SPI协议后,可以使用这条命令来进行测试。

请添加图片描述

(3) READ

发送指令和读取的首地址后,接下来就是读取数据,每读取一个数据,地址就会知道加一,当达到地址边界后,地址会自动跳转到0地址进行读取,读取数据的数量没有限制,可以无限读下去。读完最后一个字节后,只需要将S信号拉高即可结束本部读取操作。
请添加图片描述

(4) PP

对flash进行写入操作,以页为单位,每一页有256bytes,共8192页,具体如下。

请添加图片描述

然后时序图如下,

请添加图片描述

然后重点来了,意思就是,在执行PP命令的时候,需要先执行WREN命令,并且锁住,可能在PP命令结束后,WREN命令会失效,根据最后一行推测出来的,测试时确实如此。

请添加图片描述

请添加图片描述

(5) SE

擦除命令,很简单,给指令和地址后,就ok了,擦除完成后,再次读取的时候,应该是全FF。同样擦除命令和PP命令一样,需要先发送WREN命令才行。其他的就没了。

请添加图片描述

请添加图片描述

四. 其他注意事项

Tcsh : 表示两条指令间隔时间,

Tpp: 表示page program后,需要等待Tpp时间后,才能进行下一条指令的操作

Tse: 同Tpp一样。
请添加图片描述

请添加图片描述

五. Verilog实现

写流程: 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 interfaceinput   wire            sys_clk,input   wire            sys_rstn,//user inferface//user readinput   wire            read_req,output  wire[7:0]       read_data,output  wire            read_ack,//user writeinput   wire            write_req,input   wire[7:0]       write_data,output  wire            write_ack,//spi to external flashoutput  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) beginif (sys_rstn == 1'b0)state <= SPI_IDLE;elsestate <= next_state;
endalways@(*)begincase (state)SPI_IDLE: if( write_req == 1'b1  || read_req == 1'b1)next_state <= SPI_DATA;elsenext_state <= SPI_IDLE;SPI_DATA:if( spi_rev_send_bit_cnt == 'd7 && spi_clk_inverse_cnt == 1'b1)next_state <= SPI_END;elsenext_state <= SPI_DATA;SPI_END:next_state <= SPI_END2;SPI_END2:next_state <= SPI_IDLE;default: next_state <= SPI_IDLE; endcase
endalways @(posedge sys_clk or negedge sys_rstn) beginif( 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;elsespi_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)beginif( 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;elsespi_rev_send_bit_cnt <= 4'd0;
end//mosi data shift
always @(posedge sys_clk or negedge sys_rstn) beginif( 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]};elsewrite_data_reg <= write_data_reg;
end//spi_clk_gen
always@(posedge sys_clk or negedge sys_rstn)beginif( 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)beginif( 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)beginif( 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};elseread_data_reg <= read_data_reg;
endendmodule

Flash模块

//flash  write/read control   : M25P16
module flash_contorl (//system interfaceinput   wire            sys_clk,input   wire            sys_rstn,//user interface//read identificationinput   wire            read_id_req,output  wire[23:0]      flash_id,output  wire            read_id_end,//read datainput   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 enalbeinput   wire            write_enable_req,output  wire            write_enable_end,//write disalbe//write datainput   wire            write_req, input   wire[23:0]      write_page,     //write num pageinput   wire[8:0]       write_size,     //max equal 256input   wire[7:0]       write_data,output  wire            write_ack,output  wire            write_end,//erase sectorinput   wire            erase_sector_req,input   wire[23:0]      erase_sector_addr,output  wire            erase_sector_end,//erase bulkinput   wire            erase_bulk_req,output  wire            erase_bulk_end,//spi to external flashoutput  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'd1localparam  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      13slocalparam  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 csnassign      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)beginif( sys_rstn == 1'b0)state <= Flash_Idle;elsestate <= next_state;
endalways@(*)begincase (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;elsenext_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;elsenext_state <= Flash_Read_Identification;Flash_Write_Enable:if( spi_write_ack == 1'b1)next_state <= Flash_Wait;elsenext_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;elsenext_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;elsenext_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;elsenext_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;elsenext_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;elsenext_state <= Flash_Wait;Flash_End:next_state <= Flash_Idle;default:    next_state <= Flash_Idle;endcase
endalways@(posedge sys_clk or negedge sys_rstn ) beginif( 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;elsestate_ts <= Flash_Idle; elsestate_ts <= state_ts;
endalways@(posedge sys_clk or negedge sys_rstn)beginif( 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 )beginif( 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;elsespi_csn_reg <= 1'b0;
end
//spi read/write byte cnt
always@(posedge sys_clk or negedge sys_rstn)beginif( 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)beginif( 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;elsespi_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;elsespi_read_req <= 1'b1;elsespi_read_req <= 1'b0;
end//read flash identification
always@(posedge sys_clk or negedge sys_rstn)beginif( 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};elseflash_id_reg <= flash_id_reg;elseflash_id_reg <= flash_id_reg;
end//spi write req
always@(posedge sys_clk or negedge sys_rstn)beginif( 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;elsespi_write_req <= 1'b0;
end//spi write data
always@(posedge sys_clk or negedge sys_rstn)beginif( 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;endcaseelse 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];elsespi_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];elsespi_write_data <= erase_sector_addr[7:0];else if( state == Flash_Bulk_Erase)spi_write_data <= `Bulk_Erase;elsespi_write_data <= 8'd0;
endSPI_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

下面给出测试读取ID的值
在这里插入图片描述
完整工程 可以 关注 回复 FPGA读写Flash 获取

相关内容

热门资讯

台湾宜兰县海域发生6.6级地震... 央视新闻消息,中国地震台网正式测定:12月27日23时05分在台湾宜兰县海域(北纬24.67度,东经...
一杯热牛奶的治愈力:乳业如何在... 随着气候的异常,这个冬天很多人都感到忽冷忽热,免疫力下降,数据也证明了这一点。2025年第49周中国...
全国八成帝王蟹,竟来自这个东北... 订阅 快刀财经 ▲ 做您的私人商学院帝王蟹的红火给这座边境城市带来了生机。作者 :朱秋雨来源:盐财经...
Omdia:三季度印度智能手机... 10月22日消息,Omdia最新研究数据显示,2025年第三季度,印度智能手机市场同比增长3%,出货...
全球首个机场出发层 VPD 启... 6月14日消息,全球首个支持机场出发层泊车代驾VPD落地广州白云机场,用户开着传祺向往M8乾崑系列 ...
同力股份:无人驾驶产品目前已完... 新京报贝壳财经讯(记者黄鑫宇)2025年12月26日晚,北交所上市公司陕西同力重工股份有限公司(即“...
新消费周报 | 淘宝闪购开通无... 《CBNData新消费周报》精选本周新消费领域最新动态,公司头条、消费风向、智能创新、营销动态、可持...
穿越波动的稳健之选:优质可转债... 作者:烟雨导语:在当下复杂多变的市场环境中,可转债因其"股债双性"的独特优势,成为投资者平衡风险与收...
6天4板!华联控股拟12.35... Arizaro项目LCE(碳酸锂当量)总资源量概要。 图源:华联控股公告本报记者 李贝贝 上海报道1...
触及重大违法行为,或将被退市!... 本文来源:时代周报 作者:林昀肖12月26日,*ST长药(300391.SZ)公告称,公司因涉嫌定期...