Flash基础入门之flash读写学习笔记与spi接口及简单测试验证
小标 2018-08-24 来源 : 阅读 1372 评论 0

摘要:本文主要向大家介绍了Flash基础入门之flash读写学习笔记与spi接口及简单测试验证,通过具体的内容向大家展现,希望对大家学习Flash基础入门有所帮助。

本文主要向大家介绍了Flash基础入门之flash读写学习笔记与spi接口及简单测试验证,通过具体的内容向大家展现,希望对大家学习Flash基础入门有所帮助。

          FPGA中的视频图像资源,以及想要永久存储的程序都是要存储在flash中,flash是FPGA一个不可缺少的部分,flash的种类有很多,根据winbond公司的128Mbit Qual SPI接口的flash,型号为W25Q128BV,作为初学者根据现有的资料去学习,下面的内容主要以这款芯片作参考。前面也提到了三大串行数据传输模式UART,I2C,SPI,顺道就把SPI的内容也做一下总结,每篇一句话,带着自己的思考看问题,尽信书不如无书,fighting!!!
         一、flash简单分类
         flash分为nor flash和nand flash。nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。NOR Flash的读取,用户可以直接运行装载在NOR FLASH里面的代码。NAND Flash没有采取内存RAM的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。
        二、SPI接口
       SPI(serial peripheral Interface)串行外设接口总线系统是一种同步串行外设接口,使MCU与各种外围设备以串行方式进行通信以交换信息。SPI接口主要应用在EEPROM、FLASH、实时时钟、AD转换器,还有数字信号处理器和数字信号解码器。在CPU和外围低速器件之间进行同步串行数据传输,数据按位传输,低位在前,高位在后,全双工通信。
     三、QSPI FLASH硬件介绍

 Flash容量由65536个256-byte的page组成,三种擦除方式。一种为Sector(16个page,共4KB),一种为Block擦除(128个page,共32KB),另一种为Chip擦除(整个擦除)。连接的管脚有QSPI_CS, QSPI_CLK, QSPI_MISO0, QSPI_MISO1, QSPI_MISO2, QSPI_MISO3。下面是一些SPI用到的几个命令。
(1)读 Manufacturer/Device ID(90h)先发送命令字90,再发送24位的地址(全0),然后接收2个byte的数据(第一个数据是 Manufacturor:FEh,第二个是设备的 Device ID:17h),数据在时钟的上升沿采样。

 (2)  Sector 擦除(20)  先发送命令字 20,再发送 24 位的地址。数据都在时钟的上升沿采样。
(3)先发送命令字 05,然后接收 16 位的寄存器数据。数据都在时钟的上 升沿采样。

   (4)先发送命令字 02,再发送 24 位的地址,然后写入 256 个编程的数据(数 据的数量可以自己修改, 但不能超过 256 个)。数据都在时钟的上升沿采样。
  
 (5)先发送命令字 03,再发送 24 位的地址,然后接收数据。数据在时钟的上升 沿采样。
 
四、程序设计
实现FLASH设备ID的读取,Sector擦除,Page编程,数据的读取。由一个顶层设计模块和一个子模块组成,同样通过程序中加入自己的笔记,红色为所加。SPI 通信程序按照 FLASH 的 SPI 时序把并行的命令,地址或数据转成串行的数据从 SPI 发 送给 FLASH;在读的时候接收 SPI 的串行的数据转化为并行的数据。这里需要注意的是 SPI 发送数据的时候,数据是在时钟的下降沿改变的。所以读取数据时,是要在时钟的上升沿读取。

  1 `timescale 1ns / 1ps
  2 //////////////////////////////////////////////////////////////////////////////////
  3 // Module Name:    flash_spi 
  4 //////////////////////////////////////////////////////////////////////////////////
  5 module flash_spi(
  6                   output flash_clk,
  7                         output reg flash_cs,  //使能信号
  8                         output reg flash_datain,
  9                         input  flash_dataout,
 10                         
 11                   input clock25M,
 12                         input flash_rstn,
 13                         input [3:0] cmd_type,
 14                         output reg Done_Sig,
 15                         input [7:0]  flash_cmd,
 16                         input  [23:0] flash_addr,
 17                         output reg [7:0] mydata_o,
 18                         output myvalid_o,
 19                         output reg [2:0] spi_state
 20             
 21                         );
 22 
 23 
 24 
 25 assign myvalid_o=myvalid;
 26 
 27 assign flash_clk=spi_clk_en?clock25M:0;
 28 
 29 
 30 reg myvalid;
 31 reg [7:0] mydata;
 32 
 33 reg spi_clk_en=1'b0;
 34 reg data_come;
 35 
 36 
 37 parameter idle=3'b000;
 38 parameter cmd_send=3'b001;
 39 parameter address_send=3'b010;
 40 parameter read_wait=3'b011;
 41 parameter write_data=3'b101;
 42 parameter finish_done=3'b110;
 43 
 44 
 45 
 46 reg [7:0] cmd_reg;    //cmd_reg作为flash_cmd输入命令信号的寄存器,有输入信号时与flash_cmd一样都为寄存器变量,但寄存器内部数值可能发生变化。
 47 reg [23:0] address_reg;  //address_reg同样作为flash_addr信号的内部寄存器。
 48 reg [7:0] cnta;          //自己的理解,cnta是一个8位的寄存器变量,代表存储的是一个数值,这个数值在此时用来表示串转并或者并转串的某一位位数数值,同理下面的cntb。
 49 reg [8:0] write_cnt;
 50 reg [7:0] cntb;
 51 reg [8:0] read_cnt;
 52 reg [8:0] read_num;
 53 
 54 reg read_finish;
 55 
 56 //发送读flash命令
 57 always @(negedge clock25M)   //读数据在时钟的下降沿
 58 begin
 59 if(!flash_rstn)
 60     begin
 61         flash_cs<=1'b1;        
 62         spi_state<=idle;
 63         cmd_reg<=0;
 64         address_reg<=0;
 65        spi_clk_en<=1'b0;                //SPI clock输出不使能
 66         cnta<=0;
 67       write_cnt<=0;
 68       read_num<=0;    
 69       address_reg<=0;
 70         Done_Sig<=1'b0;
 71     end
 72 else
 73     begin
 74     case(spi_state)
 75         idle: begin              
 76                 spi_clk_en<=1'b0;
 77                 flash_cs<=1'b1;
 78                 flash_datain<=1'b1;    
 79                cmd_reg<=flash_cmd;
 80             address_reg<=flash_addr;
 81               Done_Sig<=1'b0;                
 82                 if(cmd_type[3]==1'b1) begin                //如果flash操作命令请求
 83                         spi_state<=cmd_send;
 84                   cnta<=7;        
 85                   write_cnt<=0;
 86                   read_num<=0;                    
 87                 end
 88         end
 89         cmd_send:begin
 90                spi_clk_en<=1'b1;                          //flash的SPI clock输出
 91                 flash_cs<=1'b0;                            //cs拉低
 92                if(cnta>0) begin                           //如果cmd_reg还没有发送完
 93                         flash_datain<=cmd_reg[cnta];         //发送bit7~bit1位
 94                   cnta<=cnta-1'b1;                        
 95                 end                
 96                 else begin                                 //发送bit0
 97                         flash_datain<=cmd_reg[0];
 98                         if ((cmd_type[2:0]==3'b001) | (cmd_type[2:0]==3'b100)) begin    //如果是Write Enable/disable instruction
 99                              spi_state<=finish_done;
100                   end                             
101                   else if (cmd_type[2:0]==3'b011) begin            //如果是read register1
102                               spi_state<=read_wait;
103                              cnta<=7;
104                              read_num<=1;                            //接收一个数据
105                         end     
106                         else begin                              //如果是sector erase, page program, read data,read device ID      
107                              spi_state<=address_send;
108                             cnta<=23;
109                        end
110                 end
111         end
112         address_send:begin
113                if(cnta>0)  begin                                 //如果cmd_reg还没有发送完
114                     flash_datain<=address_reg[cnta];               //发送bit23~bit1位
115                cnta<=cnta-1;                        
116                 end                
117                 else begin                                        //发送bit0
118                     flash_datain<=address_reg[0];   
119                if(cmd_type[2:0]==3'b010) begin                 //如果是    sector erase
120                           spi_state<=finish_done;    
121                end
122                else if (cmd_type[2:0]==3'b101) begin              //如果是page program                
123                        spi_state<=write_data;
124                          cnta<=7;                       
125                     end
126                     else if (cmd_type[2:0]==3'b000) begin                //如果是读Device ID
127                         spi_state<=read_wait;
128                          read_num<=2;                                     //接收2个数据的Device ID
129                end                         
130                     else begin
131                         spi_state<=read_wait;
132                          read_num<=256;                                  //接收256个数据                             
133                end                         
134                 end
135         end
136         read_wait: begin
137              if(read_finish)  begin
138                  spi_state<=finish_done;
139                   data_come<=1'b0;
140               end
141               else
142                  data_come<=1'b1;
143         end        
144         write_data: begin
145            if(write_cnt<256) begin                       // program 256 byte to flash
146                if(cnta>0) begin                           //如果data还没有发送完
147                         flash_datain<=write_cnt[cnta];           //发送bit7~bit1位
148                   cnta<=cnta-1'b1;                        
149                 end                
150                 else begin                                 
151                         flash_datain<=write_cnt[0];         //发送bit0
152                        cnta<=7;
153                        write_cnt<=write_cnt+1'b1;
154                 end
155             end
156          else begin
157                  spi_state<=finish_done;
158                  spi_clk_en<=1'b0;
159             end
160                  
161         end
162         finish_done:begin
163               flash_cs<=1'b1;
164               flash_datain<=1'b1;
165               spi_clk_en<=1'b0;
166               Done_Sig<=1'b1;
167               spi_state<=idle;
168         end
169         default:spi_state<=idle;
170         endcase;        
171     end
172 end
173     
174 //接收flash数据,把SPI接收的串行数据转成8位字节    
175 always @(posedge clock25M)
176 begin
177     if(!flash_rstn)begin
178             read_cnt<=0;
179             cntb<=0;
180             read_finish<=1'b0;
181             myvalid<=1'b0;
182             mydata<=0;
183             mydata_o<=0;
184     end
185     else
186          if(data_come)   begin
187                 if(read_cnt<read_num) begin  //接收read_num个数据              
188                         if(cntb<7) begin                    //接收一个byte的bit0~bit6          
189                              myvalid<=1'b0;
190                              mydata<={mydata[6:0],flash_dataout};
191                              cntb<=cntb+1'b1;
192                         end
193                         else  begin
194                              myvalid<=1'b1;          //一个byte数据有效
195                              mydata_o<={mydata[6:0],flash_dataout};  //接收bit7
196                              cntb<=0;
197                              read_cnt<=read_cnt+1'b1;
198                         end
199                 end                              
200                 else begin 
201                      read_cnt<=0;
202                      read_finish<=1'b1;
203                      myvalid<=1'b0;
204                 end
205             end
206          else begin
207               read_cnt<=0;
208               cntb<=0;
209               read_finish<=1'b0;
210               myvalid<=1'b0;
211               mydata<=0;
212          end
213 end            
214 
215 endmodule

顶层控制程序

module flash_test
(
    input          CLK,
     input          RSTn,


    output flash_clk,
     output flash_cs,
     output flash_datain,
     input  flash_dataout
     

);


     
/*******************************/
     
reg [3:0] i;
reg [7:0] flash_cmd;
reg [23:0] flash_addr;

reg clock25M;
reg [3:0] cmd_type;

reg [7:0] time_delay;

wire Done_Sig;
wire [7:0] mydata_o;
wire myvalid_o;
wire [2:0] spi_state;


/*******************************/
//FLASH 擦除,Page Program,读取程序    
/*******************************/
always @ ( posedge clock25M or negedge RSTn )
    if( !RSTn ) begin
            i <= 4'd0;
            flash_addr <= 24'd0;
            flash_cmd <= 8'd0;
            cmd_type <= 4'b0000;
            time_delay<=0;
     end
     else 
         case( i )

          4'd0://读Device ID
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h90; flash_addr <= 24'd0; cmd_type <= 4'b1000; end    
          
          4'd1://写Write Enable instruction
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h06; cmd_type <= 4'b1001; end
    
            4'd2://Sector擦除
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type<=4'b0000; end
            else begin flash_cmd <= 8'h20; flash_addr <= 24'd0; cmd_type <= 4'b1010; end
            
          4'd3://waitting 100 clock
            if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end
            else begin i <= i + 1'b1; time_delay<=0; end    
                    
            4'd4://读状态寄存器1, 等待idle
            if( Done_Sig ) begin 
                if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
                 else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end
            end
            else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end

          4'd5://写Write disable instruction
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h04; cmd_type <= 4'b1100; end            

            4'd6://读状态寄存器1, 等待idle
            if( Done_Sig ) begin 
                if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
                 else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end
            end
            else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end

          4'd7://写Write Enable instruction
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h06; cmd_type <= 4'b1001; end 

          4'd8://waitting 100 clock
            if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end
            else begin i <= i + 1'b1; time_delay<=0; end    

          4'd9://page program: write 0~255 to flash
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1;cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h02; flash_addr <= 24'd0; cmd_type <= 4'b1101; end
            
          4'd10://waitting
            if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end
            else begin i <= i + 1'b1; time_delay<=0; end    

            4'd11://读状态寄存器1, 等待idle
            if( Done_Sig ) begin 
                if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
                 else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end
            end
            else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end

          4'd12://写Write disable instruction
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h04; cmd_type <= 4'b1100; end        

            4'd13://读状态寄存器1, 等待idle
            if( Done_Sig ) begin 
                if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
                 else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end
            end
            else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end
                    
            4'd14://read 256byte
            if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end
            else begin flash_cmd <= 8'h03; flash_addr <= 24'd0; cmd_type <= 4'b1110; end

            4'd15://idle
            i <= 4'd15;

          endcase
            
 /*****************************/              
always @ ( posedge CLK )
    if( !RSTn ) clock25M<=1'b0;
     else clock25M <= ~clock25M;


 /*****************************/
      
flash_spi U1
(
         .flash_clk(flash_clk ),
          .flash_cs( flash_cs ),
          .flash_datain( flash_datain ),  
          .flash_dataout( flash_dataout ),    
          
          .clock25M( clock25M ),           //input clock
          .flash_rstn( RSTn ),             //input reset 
          .cmd_type( cmd_type ),           // flash command type          
          .Done_Sig( Done_Sig ),           //output done signal
          .flash_cmd( flash_cmd ),         // input flash command 
          .flash_addr( flash_addr ),       // input flash address 
          .mydata_o( mydata_o ),           // output flash data 
          .myvalid_o( myvalid_o ),          // output flash data valid         
        .spi_state(spi_state)          
          

);
     
wire [35:0]   CONTROL0;
wire [255:0]  TRIG0;
chipscope_icon icon_debug (
    .CONTROL0(CONTROL0) // INOUT BUS [35:0]
);

chipscope_ila ila_filter_debug (
    .CONTROL(CONTROL0), // INOUT BUS [35:0]
   // .CLK(dma_clk),      // IN
    .CLK(CLK),      // IN
    .TRIG0(TRIG0)      // IN BUS [255:0]
    //.TRIG_OUT(TRIG_OUT0)
);                                                     

assign  TRIG0[7:0]=mydata_o;                                               
assign  TRIG0[8]=myvalid_o;   
assign  TRIG0[12:9]=i;        
assign  TRIG0[15:13]=spi_state;   
assign  TRIG0[16]=Done_Sig;   
assign  TRIG0[17]=flash_datain;  
assign  TRIG0[18]=flash_dataout;  
assign  TRIG0[19]=flash_cs;  
assign  TRIG0[20]=flash_clk; 


endmodule

 五、测试验证
      flash验证如上面读取器件ID还是需要有实际flash才能较好的获得验证结果,下面的验证程序用来作为验证数据读取,人工给予flash_dataout数据.

 1 `timescale 1ns/10ps
 2 module flash_test();
 3  wire flash_clk;
 4 wire flash_cs;
 5 wire flash_datain;
 6 reg flash_dataout;
 7 reg clock25M;
 8 reg RSTn;
 9 reg [3:0] cmd_type;
10 wire Done_Sig;
11 reg [7:0] flash_cmd;
12 reg [23:0] flash_addr;
13 wire [7:0] mydata_o;
14 wire myvalid_o;
15 wire [2:0] spi_state;
16 
17       
18      flash_spi U1
19       (
20                .flash_clk(flash_clk ),
21                 .flash_cs( flash_cs ),
22                 .flash_datain( flash_datain ),  
23                 .flash_dataout( flash_dataout ),    
24                 
25                 .clock25M( clock25M ),           //reg clock
26                 .flash_rstn( RSTn ),             //reg reset 
27                 .cmd_type( cmd_type ),           // flash command type          
28                 .Done_Sig( Done_Sig ),           //reg done signal
29                 .flash_cmd( flash_cmd ),         // reg flash command 
30                 .flash_addr( flash_addr ),       // reg flash address 
31                 .mydata_o( mydata_o ),           // reg flash data 
32                 .myvalid_o( myvalid_o ),          // reg flash data valid         
33               .spi_state(spi_state)          
34                       
35       );
36       initial  clock25M <= 1'b0;
37       always   #20 clock25M <= ~clock25M;
38 
39   initial
40      begin
41             RSTn<= 1'b0;
42             flash_addr <= 24'd0;
43             flash_cmd <= 8'd0;
44             cmd_type <= 4'b0000;
45     #100   RSTn <= 1'b1;
46     #100 begin flash_cmd <= 8'h03; flash_addr <= 24'd0; cmd_type <= 4'b1110; end    //此处并没有定义flash_dataout数据,可考虑采用随机数产生函数来获取,程序内数据为256位,可适当减小验证
47     
48     #83300  RSTn<= 1'b0;
49     
50     #100  $stop;
51     end
52             
53 
54 endmodule

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标常用软件Flash频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程