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读写学习笔记与spi接口及简单测试验证

 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),数据在时钟的上升沿采样。

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

 (2)  Sector 擦除(20)  先发送命令字 20,再发送 24 位的地址。数据都在时钟的上升沿采样。flash读写学习笔记与spi接口及简单测试验证

(3)先发送命令字 05,然后接收 16 位的寄存器数据。数据都在时钟的上 升沿采样。

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

   (4)先发送命令字 02,再发送 24 位的地址,然后写入 256 个编程的数据(数 据的数量可以自己修改, 但不能超过 256 个)。数据都在时钟的上升沿采样。

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

 (5)先发送命令字 03,再发送 24 位的地址,然后接收数据。数据在时钟的上升 沿采样。

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

四、程序设计

实现FLASH设备ID的读取,Sector擦除,Page编程,数据的读取。由一个顶层设计模块和一个子模块组成,同样通过程序中加入自己的笔记,红色为所加。SPI 通信程序按照 FLASH 的 SPI 时序把并行的命令,地址或数据转成串行的数据从 SPI 发 送给 FLASH;在读的时候接收 SPI 的串行的数据转化为并行的数据。这里需要注意的是 SPI 发送数据的时候,数据是在时钟的下降沿改变的。所以读取数据时,是要在时钟的上升沿读取。

flash读写学习笔记与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

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

顶层控制程序

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

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读写学习笔记与spi接口及简单测试验证

 五、测试验证

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

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

 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读写学习笔记与spi接口及简单测试验证

 

 验证结果如下

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

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

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

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

 

转自:https://www.cnblogs.com/uiojhi/p/7604221.html