FPGA外置QSPI Flash读写操作详解

1.写在前面

FPGA内部不具有掉电存储程序的功能,所以都需要外置的flash存储器来存储程序,上电后从flash加载程序到FPGA中运行。外置的flash可以存储程序,也可以存储任何用户数据,可以更有效的利用flash的存储空间。

值得注意的是,用于存储程序的flash和fpga连接用的是fpga的专用引脚,flash时钟信号不可以直接驱动,这个信号是fpga硬件直接管理的,需要使用原语才可以驱动时钟信号,这个原语叫STARTUPE2。

STARTUPE2 #(
.PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams.
.SIM_CCLK_FREQ(0.0) // Set the Configuration Clock Frequency(ns) for simulation
)
STARTUPE2_inst
(
.CFGCLK(), // 1-bit output: Configuration main clock output
.CFGMCLK(), // 1-bit output: Configuration internal oscillator clock output
.EOS(), // 1-bit output: Active high output signal indicating the End Of Startup.
.PREQ(), // 1-bit output: PROGRAM request to fabric output
.CLK(0), // 1-bit input: User start-up clock input
.GSR(0), // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name)
.GTS(0), // 1-bit input: Global 3-state input (GTS cannot be used for the port name)
.KEYCLEARB(1), // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM)
.PACK(1), // 1-bit input: PROGRAM acknowledge input
.USRCCLKO(flash_clk), // 1-bit input: User CCLK input
.USRCCLKTS(0), // 1-bit input: User CCLK 3-state enable input
.USRDONEO(1), // 1-bit input: User DONE pin output control
.USRDONETS(1) // 1-bit input: User DONE 3-state enable output
)

里边的flash_clk就是要约束的flash时钟信号,通过这个原语才能约束用于配置程序的flash的时钟信号。

不想这么麻烦也可以,在硬件上再加一片flash接到通用管脚上就完了,但是想做远程程序更新功能就必须这么干,通过串口或者网口或者什么其他通信接口将要更新的程序发送至fpga,fpga存储空间不够的话就暂存到ddr或者外置sram里,再按顺序操作flash烧入,扯远了,有机会一定要搞一下这个功能肯定有用,只是我现在还不会写上位机。

2.FPGA实现qspi flash读写

前面扯了很多没用的,直接上源码,自己写的,为了省事没搞qspi只用了spi,智商感人的领导把时间全给硬件开发了,硬件平台搭了一年多,软件只给了3个月,交货前2个月才知道具体需求,之前全是含糊其辞,没办法全是赶工出来的,凑活看吧,改改就能用,你问我为啥敢吐槽,已经被辞了,项目干的甲方无比懊恼,钱都不想给了,领导肯定是没错的,所以嘛只能这样了,硬件做的一言难尽时间全耽误了硬件平台到我手里连4个月都没有中间还反复改硬件,硬件做不到了就拿程序填,硬件做的垃圾最后全是做软件的背了锅,所以最后只有我走人,做垃圾的还好好的呢,说多了直接上码,前面发的牢骚各位直接忽略。

顶层命令控制

///
/* Document info
document class : RES
module name    : flash
module purpose : n25q256 256byte per page
version        : V1.0
author         : mayidianzi
*/
///


module flash_ctrl(
   input  I_clk            , //  clk
   input  I_TEST_CLK       ,
   input  I_reset_n        , // module reset signal
   input  I_read_en        , 
   input  I_write_en       ,
   input  I_ID_en          ,
   input  I_erase_en       ,
   input [63:0] I_wr_data  ,
   input  I_wraddr_clear   ,
   input  I_rdaddr_clear   ,
   output reg O_flash_ready,
   output reg O_flash_end  ,
   input  I_SDO         , // flash D1信号     
   output O_SDI         , // flash D0信号 
   output O_SCK         , // flash SCK信号 
   output O_CS          ,
   input  I_flash_dq3   ,
   input  I_flash_dq2   ,
   output reg [63:0] O_rd_data,
   output reg [23:0] O_flash_id
    );
    
/  parameter set  \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\            
parameter READ_STATUS_REGISTER      = 16'h05_00,
           READ_ID                   = 48'h9F_00_00_00_00_00,
           WRITE_ENABLE              = 8'h06,
           BULK_ERASE                = 8'hC7;
parameter C_empty        = 8'd12,
          C_READID_idle   = 8'd0,
          C_READID_valid  = 8'd1,
          C_erase_idle    = 8'd2,
          C_erase_valid   = 8'd3,
          C_check_idle    = 8'd4,
          C_check_valid   = 8'd5,
          C_write_idle    = 8'd6,
          C_write_valid   = 8'd7,
          C_read_idle     = 8'd8,
          C_read_valid    = 8'd9,
          C_wrenable_idle = 8'd10,
          C_wrenable_valid= 8'd11;

  internal signal  \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
reg  [23:0] S_rd_addr        ;
reg  [23:0] S_wr_addr        ;
wire [95:0] S_rd_data       ;
reg  [63:0] S_wr_data        ;
wire [15:0] S_rd_status     ;
wire [95:0] READ_Command    ;
wire [95:0] WRITE_Command   ;
reg  [7:0] S_status          ;
reg  [1:0] S_w_e_flag        ;
reg  S_readID_start          ;
reg  S_read_start            ;
reg  S_write_start           ;
reg  S_erase_start           ;
reg  S_wrenable_start        ;
reg  S_check_start           ;
wire S_end                  ;
wire S_busy                 ;
wire [31:0] S_read_id       ;
reg  S_rdaddr_clear, S_wraddr_clear;

assign READ_Command = {8'h03,S_rd_addr,64'h00_00_00_00_00_00_00_00};
assign WRITE_Command = {8'h02,S_wr_addr,S_wr_data};
assign S_busy = S_rd_status[0]; 
      instance       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//ila_1 ila_1_i(
//.clk(I_clk),
//.probe0(S_rd_addr),
//.probe1(S_wr_addr),
//.probe2(I_wraddr_clear),
//.probe3(I_rdaddr_clear),
//.probe4(O_rd_data),
//.probe5(I_wr_data),
//.probe6(I_write_en)
//);

spi_port spi_port_i(
    .I_CLK(I_clk),
    .I_reset(I_reset_n),
    .I_wrcmd(WRITE_Command),
    .I_rdcmd(READ_Command),
    .I_erasecmd(BULK_ERASE),
    .I_rdIDcmd(READ_ID),
    .I_wrencmd(WRITE_ENABLE),
    .I_checkcmd(READ_STATUS_REGISTER),
    .I_wrstart(S_write_start),
    .I_rdstart(S_read_start),
    .I_readID_start(S_readID_start),
    .I_erase_start(S_erase_start),
    .I_wrenable_start(S_wrenable_start),
    .I_check_start(S_check_start),
    .I_SDO(I_SDO),
    .O_end(S_end),
    .O_SCK(O_SCK),
    .O_CS(O_CS),
    .O_SDI(O_SDI),
    .I_flash_dq2(I_flash_dq2),
    .I_flash_dq3(I_flash_dq3),
    .O_rddata(S_rd_data),
    .O_rd_status(S_rd_status),
    .O_rd_id(S_read_id)
   );
    
    main programe    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

/*          config procedure        */
always @(posedge I_clk or negedge I_reset_n) 
begin
    if(!I_reset_n)
     begin
    S_status <= C_check_idle;
    O_flash_ready <= 0;
    S_w_e_flag    <= 0;
    O_flash_end <= 0;
   end
    else
     begin
   case(S_status)
         C_empty:
          begin           
           if(I_ID_en == 1)
             begin
               S_status <= C_READID_idle;
               O_flash_ready <= 0;
               O_flash_end <= 0;
             end
           else if(I_erase_en == 1)
             begin
                 S_status <= C_wrenable_idle;
               O_flash_ready <= 0;
               S_w_e_flag    <= 1;
               O_flash_end <= 0;
             end
           else if(I_read_en == 1)
             begin
                 S_status <= C_read_idle;
               O_flash_ready <= 0;
               O_flash_end <= 0;
             end
           else if(I_write_en == 1)
             begin
                 S_status <= C_wrenable_idle;
               O_flash_ready <= 0;
               S_w_e_flag    <= 2;
               O_flash_end <= 0;
             end
           else
             begin
                 O_flash_ready <= 1;
                 O_flash_end <= 0;
                 S_w_e_flag    <= 0;
                 S_status <= C_empty;
             end 
          end
           C_READID_idle:
            begin                
                 S_status <= C_READID_valid;
            end
           C_READID_valid:
            begin
                if(S_end == 1)
                 begin
                  S_status <= C_empty;
                  O_flash_end <= 1;
                 end
            end
           C_wrenable_idle:
            begin
                 S_status <= C_wrenable_valid;
            end
           C_wrenable_valid:
            begin
                if(S_end == 1)
                 begin
                     if(S_w_e_flag == 1)
                      S_status <= C_erase_idle;
                     else if(S_w_e_flag == 2)
                      S_status <= C_write_idle;
                 end                
            end
           C_erase_idle:
            begin
                S_status <= C_erase_valid;
            end
           C_erase_valid:
            begin
                if(S_end == 1)
                 begin
                  S_status <= C_check_idle;
                  S_w_e_flag <= 0;
                 end
            end
           C_check_idle:
            begin
                S_status <= C_check_valid;
            end
           C_check_valid:
            begin
                if(S_end == 1)
                  begin
                   if(S_busy == 1)
                     S_status <= C_check_idle;
                   else
                     begin
                       S_status <= C_empty;
                       O_flash_end <= 1;
                     end
                  end
            end
           C_read_idle:
            begin
                S_status <= C_read_valid;
            end
           C_read_valid:
            begin
                if(S_end == 1)
                 S_status <= C_check_idle;
            end
           C_write_idle:
            begin
                S_status <= C_write_valid;
            end
           C_write_valid:
            begin
                if(S_end == 1)
                 begin
                  S_status <= C_check_idle;
                  S_w_e_flag <= 0;
                 end
            end           
           default:
            begin
                S_status = C_empty;
                S_w_e_flag <= 0;
                O_flash_ready <= 0;
                O_flash_end <= 0;
            end
       endcase
   end
end

always @(posedge I_clk or negedge I_reset_n) 
begin
    if(!I_reset_n)
     begin
     S_readID_start <= 0;
     S_read_start   <= 0;
     S_write_start  <= 0;
     S_erase_start  <= 0;
     S_wrenable_start <= 0;
     S_check_start  <= 0;
     O_rd_data <= 0;
     O_flash_id <= 0;
     end
    else
     begin
         case(S_status)
             C_empty:
              begin
                  S_readID_start <= 0;
        S_read_start   <= 0;
        S_write_start  <= 0;
        S_erase_start  <= 0;
        S_wrenable_start <= 0;
        S_check_start  <= 0;
              end
           C_READID_idle:
            begin
                S_readID_start <= 1;
            end
           C_READID_valid:
            begin
                S_readID_start <= 0;
                if(S_end == 1)
                  O_flash_id <= S_read_id[23:0];
            end
           C_wrenable_idle:
            begin
                S_wrenable_start <= 1;
            end
           C_wrenable_valid:
            begin
                S_wrenable_start <= 0;
            end
           C_erase_idle:
            begin                
        S_erase_start  <= 1;
            end
           C_erase_valid:
            begin
                S_erase_start  <= 0;
            end
           C_check_idle:
            begin
                S_check_start  <= 1;
            end
           C_check_valid:
            begin
                S_check_start  <= 0;
            end
           C_read_idle:
            begin
                S_read_start   <= 1;
            end
           C_read_valid:
            begin
                S_read_start   <= 0;
        if(S_end == 1)
         O_rd_data <= S_rd_data[63:0];
            end
           C_write_idle:
            begin
                S_write_start <= 1;
                S_wr_data <= I_wr_data;
            end
           C_write_valid:
            begin
                S_write_start <= 0;
            end
           default:
            begin
                S_readID_start <= 0;
        S_read_start   <= 0;
        S_write_start  <= 0;
        S_erase_start  <= 0;
        S_wrenable_start <= 0;
        S_check_start  <= 0;
        O_rd_data <= 0;
        O_flash_id <= 0;
            end
       endcase
   end
end

always @(posedge I_clk or negedge I_reset_n)
begin
    if(!I_reset_n)
     begin
         S_wraddr_clear <= 0;
         S_rdaddr_clear <= 0;
     end
    else
     begin
        S_wraddr_clear <= I_wraddr_clear;
         S_rdaddr_clear <= I_rdaddr_clear;
     end
end

always @(posedge I_clk or negedge I_reset_n)
begin
    if(!I_reset_n)
     begin
         S_rd_addr <= 0;
     end
    else
     begin
       case(S_status)
            C_read_valid:
             if(S_end == 1)
              S_rd_addr <= S_rd_addr + 8;
            default:
             begin
              if(S_rdaddr_clear == 1)
                S_rd_addr <= 0;
             end
       endcase
     end
end

always @(posedge I_clk or negedge I_reset_n)
begin
    if(!I_reset_n)
     begin
         S_wr_addr <= 0;
     end
    else
     begin
       case(S_status)
            C_write_valid:
             if(S_end == 1)
              S_wr_addr <= S_wr_addr + 8;
            default:
             begin
              if(S_wraddr_clear == 1)
                S_wr_addr <= 0;
             end
       endcase
     end
end
endmodule

这里边的读写地址是自动管理的,每做一次读写操作自行增加,根据外部清地址信号来清零的,不想这么用的可以自行更改。我太懒不想改了。这个模块怎么操作可以自己写个仿真文件仿真一下,或者来问我,我会捡我想得起来的说说,不解释了就是太懒了。

通信底层spi

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/15 15:08:39
// Design Name: 
// Module Name: spi_wr_port
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module spi_port
   (
   input I_CLK,
   input I_reset,
   input[95:0] I_wrcmd,
   input[95:0] I_rdcmd,
   input[7:0] I_erasecmd,
   input[47:0] I_rdIDcmd,
   input[7:0] I_wrencmd,
   input[15:0] I_checkcmd,
   input I_wrstart,
   input I_rdstart,
   input I_readID_start,
   input I_erase_start,
   input I_wrenable_start,
   input I_check_start,
   input I_SDO,
   input I_flash_dq2,
   input I_flash_dq3,
   output reg O_end,
   output reg O_SCK,
   output reg O_CS,
   output reg O_SDI,
   output reg[95:0] O_rddata,
   output reg[15:0] O_rd_status,
   output reg[31:0] O_rd_id
   );
    
//
reg[47:0] S_rd_id;
reg[95:0] S_rddata;
reg[15:0] S_rd_status;
reg[15:0] S_state;
reg[31:0] S_cnt;

parameter READ_STATUS_REGISTER      = 16'h05_00,
           READ_ID                   = 48'h9F_00_00_00_00_00,
           WRITE_ENABLE              = 8'h06,
           BULK_ERASE                = 8'hC7;

parameter C_idle = 0,
          C_rdID_valid = 2,
          C_rdID_end  = 3,
          C_rdID_stop = 4,
          C_WEN_valid = 6,
          C_WEN_end  = 7,
          C_WEN_stop = 8,
          C_erase_valid = 10,
          C_erase_end  = 11,
          C_erase_stop = 12,
          C_check_valid = 14,
          C_check_end  = 15,
          C_check_stop = 16,
          C_rd_valid = 18,
          C_rd_end  = 19,
          C_rd_stop = 20,
          C_wr_valid = 22,
          C_wr_end  = 23,
          C_wr_stop = 24;    
                
//
//ila_1 ila_1_i(
//.clk(I_CLK),
//.probe0(O_rd_status),
//.probe1(O_rd_id),
//.probe2(O_CS),
//.probe3(O_SDI),
//.probe4(I_SDO),
//.probe5(O_SCK),
//.probe6(S_state),
//.probe7(I_readID_start),
//.probe8(O_rddata[63:0]),
//.probe9(I_rdcmd[95:64]),
//.probe10(I_wrcmd[95:64])
//);            
//
always @(posedge I_CLK or negedge I_reset)
begin
    if(!I_reset)
     begin
          S_state <= C_idle;
          S_cnt <= 0;
     end
    else
     begin
         case(S_state)
             C_idle:
              begin
                  S_cnt <= 0;
                  if(I_readID_start == 1)
                   S_state <= C_rdID_valid;    
                  else if(I_wrenable_start == 1)     
                   S_state <= C_WEN_valid;     
                  else if(I_erase_start == 1)
                   S_state <= C_erase_valid;
                  else if(I_check_start == 1)     
                   S_state <= C_check_valid;     
                  else if(I_rdstart == 1)
                   S_state <= C_rd_valid;
                  else if(I_wrstart == 1)
                   S_state <= C_wr_valid; 
              end
             C_rdID_valid:
              begin
                  if(S_cnt == 48*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_rdID_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_rdID_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_rdID_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_rdID_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
//
             C_WEN_valid:
              begin
                  if(S_cnt == 8*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_WEN_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_WEN_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_WEN_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_WEN_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
//
             C_erase_valid:
              begin
                  if(S_cnt == 8*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_erase_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_erase_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_erase_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_erase_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
//
             C_check_valid:
              begin
                  if(S_cnt == 16*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_check_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_check_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_check_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_check_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
//
             C_rd_valid:
              begin
                  if(S_cnt == 96*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_rd_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_rd_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_rd_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_rd_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
//
             C_wr_valid:
              begin
                  if(S_cnt == 96*2)
                   begin
                    S_cnt <= 0;
                    S_state <= C_wr_end;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_wr_end:
              begin
                  if(S_cnt == 30)
                   begin
                    S_cnt <= 0;
                    S_state <= C_wr_stop;
                   end
                  else
                   S_cnt <= S_cnt + 1;
              end
             C_wr_stop:
              begin
                  S_state <= C_idle;
                  S_cnt <= 0;
              end
             default:
              begin
                  S_state <= C_idle;
            S_cnt <= 0;
              end
      endcase
     end
end


always @(posedge I_CLK or negedge I_reset)
begin
    if(!I_reset)
     begin
          O_end <= 0;
      O_SCK <= 0;
      O_CS <= 1;
      O_SDI <= 0;
      S_rd_id <= 0;
      O_rd_id <= 0;
      S_rddata <= 0;
      O_rddata <= 0;
      S_rd_status <= 0;
      O_rd_status <= 0;
     end
    else
     begin
         case(S_state)
             C_idle:
              begin
                  O_end <= 0;    
                  O_SCK <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;              
              end
//     
             C_rdID_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                          O_SCK <= 0;
                          if(S_cnt == 48*2)
                           begin
                              O_SDI <= I_rdIDcmd[0];
                            end
                           else
                             begin
                              O_SDI <= I_rdIDcmd[47-S_cnt/2];
                             end
                       end
                      1'b1:
                       begin
                          O_SCK <= 1;
                          if(S_cnt == 48*2)
                            begin
                               O_SDI <= I_rdIDcmd[0];
                               S_rd_id[0] <= I_SDO;
                             end
                           else
                             begin
                               O_SDI <= I_rdIDcmd[47-S_cnt/2];
                               S_rd_id[47-S_cnt/2] <= I_SDO;
                             end
                       end
               endcase
              end
             C_rdID_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
                  O_rd_id <= S_rd_id[39:8];
              end
             C_rdID_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
//             
             C_WEN_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                         O_SCK <= 0;
                         if(S_cnt == 8*2)
                            O_SDI <= I_wrencmd[0];
                         else
                           O_SDI <= I_wrencmd[7-S_cnt/2];
                       end
                      1'b1:
                       begin
                         O_SCK <= 1;
                         if(S_cnt == 8*2)
                            O_SDI <= I_wrencmd[0];
                         else
                           O_SDI <= I_wrencmd[7-S_cnt/2];
                       end
                  endcase
              end
             C_WEN_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
             C_WEN_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
//     
             C_erase_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                         O_SCK <= 0;
                         if(S_cnt == 8*2)
                            O_SDI <= I_erasecmd[0];
                         else
                           O_SDI <= I_erasecmd[7-S_cnt/2];
                       end
                      1'b1:
                       begin
                         O_SCK <= 1;
                         if(S_cnt == 8*2)
                            O_SDI <= I_erasecmd[0];
                         else
                           O_SDI <= I_erasecmd[7-S_cnt/2];
                       end
                  endcase
              end
             C_erase_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
             C_erase_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
//     
             C_check_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                           O_SCK <= 0;
                          if(S_cnt == 16*2)
                              O_SDI <= I_checkcmd[0];
                          else
                             O_SDI <= I_checkcmd[15-S_cnt/2];
                       end
                      1'b1:
                       begin
                           O_SCK <= 1;
                           if(S_cnt == 16*2)
                              O_SDI <= I_checkcmd[0];
                          else
                             O_SDI <= I_checkcmd[15-S_cnt/2];
                          if(S_cnt == 16*2)
                              S_rd_status[0] <= I_SDO;
                          else
                              S_rd_status[15-S_cnt/2] <= I_SDO;
                       end
               endcase
              end
             C_check_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
                  O_rd_status <= S_rd_status;
              end
             C_check_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
//              
             C_rd_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                           O_SCK <= 0;
                           if(S_cnt == 96*2)
                              O_SDI <= I_rdcmd[0];
                          else
                             O_SDI <= I_rdcmd[95-S_cnt/2];
                       end
                      1'b1:
                       begin
                           O_SCK <= 1;
                           if(S_cnt == 96*2)
                              O_SDI <= I_rdcmd[0];
                          else
                             O_SDI <= I_rdcmd[95-S_cnt/2];
                          if(S_cnt == 96*2)
                              S_rddata[0] <= I_SDO;
                          else
                              S_rddata[95-S_cnt/2] <= I_SDO;
                        end
                   endcase
              end
             C_rd_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
                  O_rddata <= S_rddata;
              end
             C_rd_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
//              
             C_wr_valid:
              begin                  
                  O_CS <= 0;
                  case(S_cnt[0])
                      1'b0:
                       begin
                           O_SCK <= 0;
                           if(S_cnt == 96*2)
                              O_SDI <= I_wrcmd[0];
                          else
                             O_SDI <= I_wrcmd[95-S_cnt/2];
                       end
                      1'b1:
                       begin
                           O_SCK <= 1;
                           if(S_cnt == 96*2)
                              O_SDI <= I_wrcmd[0];
                          else
                             O_SDI <= I_wrcmd[95-S_cnt/2];
                       end
                  endcase
              end
             C_wr_end:
              begin
                  O_end <= 0;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
             C_wr_stop:
              begin
                  O_end <= 1;
                  O_CS <= 1;
                  O_SDI <= 0;
              end
             default:
              begin
                  O_end <= 0;
        O_SCK <= 0;
        O_CS <= 1;
        O_SDI <= 0;
        S_rd_id <= 0;
        O_rd_id <= 0;
        S_rddata <= 0;
        O_rddata <= 0;
        S_rd_status <= 0;
        O_rd_status <= 0;
              end
      endcase
     end
end

endmodule
物联沃分享整理
物联沃-IOTWORD物联网 » FPGA外置QSPI Flash读写操作详解

发表回复