• flash读写学习笔记与spi接口及简单测试验证(三)


              FPGA中的视频图像资源,以及想要永久存储的程序都是要存储在flash中,flash是FPGA一个不可缺少的部分,flash的种类有很多,根据winbond公司的128Mbit Qual SPI接口的flash,型号为W25Q128BV,作为初学者根据现有的资料去学习,下面的内容主要以这款芯片作参考。前面也提到了三大串行数据传输模式UART,I2C,SPI,顺道就把SPI的内容也做一下总结,每篇一句话,带着自己的思考看问题,尽信书不如无书,fighting!!!

             一、flash简单分类

             flash分为nor flashnand 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

     验证结果如下

     

     

     

       

  • 相关阅读:
    学习 WCF (6)学习调用WCF服务的各种方法
    WCF 基础简介
    Ext.form 表单为什么第二次就不正常显示
    面向对象
    学习 WCF (4)学会使用配置文件
    临危不乱,.Net+IIS环境经常出现的问题及排障。
    Java调用wcf
    Extjs 动态生成表格
    面向对象分析与设计的意义是什么
    WCF配置文件详解
  • 原文地址:https://www.cnblogs.com/uiojhi/p/7604221.html
Copyright © 2020-2023  润新知