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

 

 驗證結果如下

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM