xilinx在线升级+flash操作+N25Q128

 

因为使用xdc升级跳转方式,实际测试升级过程中掉电无法回退版本,开始了使用ICAP方式来进行跳转

这里使用的时钟是125MHz,是以太网时钟,

使用golden的bit和update的bit文件合成一个mcs文件,需要注意跳转地址,因为这里使用的容量大小是16M,所以取了中间位置;

把mcs文件先下载到flash,后通过以太网直接下发需要更新的bin文件覆盖到之前的update区,重新上电,重启,就可以实现升级了;

Flash操作:

说明:

 

擦除操作:收到擦除start命令--->读ID号匹配手册上的ID--->如果读ID成功--->开始进行擦除操作--->擦除一个块--->读状态寄存器不忙--->循环进行擦除操作

--->直到60个块擦完后--->IDLE

 

写操作:收到写start命令--->读ID号匹配手册上的ID--->如果读ID成功--->开始进行写操作--->1页写完后--->读状态寄存器不忙--->读出当前写入页数据--->循环写入读出(可验证数据是否写入)

--->直到4页完且读完--->IDLE

 

有的flash第一次擦不成功,所以得加一下读ID成功操作,

实际板子上使用:

module flash_top( 
    
    // input wire       sys_clk_p,    //system clock positive
    // input wire       sys_clk_n,    //system clock negative 
    
    input             clk   ,    
    input wire       rst_n ,       //reset ,low active    

    input           erase_start,
    input           write_start,

    input            fl_data_en ,
    input    [7:0]    fl_data    ,
    
    output      erase_completed,  //仿真
    output    four_pp_completed,//仿真
    
    input cmd_reply_clear_finish,
    input               miso    ,
    // output              sck     ,
    output              mosi    ,
    output              cs_n        
    
);

    // wire    erase_completed;//调试
    // wire  four_pp_completed;//调试

    wire sck;

   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_spi (
      .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(1'b0),                       // 1-bit input: User start-up clock input
      .GSR(1'b0),                       // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name)
      .GTS(1'b0),                       // 1-bit input: Global 3-state input (GTS cannot be used for the port name)
      .KEYCLEARB(1'b1),                 // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM)
      .PACK(1'b1),                      // 1-bit input: PROGRAM acknowledge input
      .USRCCLKO(sck),                   // 1-bit input: User CCLK input
      .USRCCLKTS(1'b0),                 // 1-bit input: User CCLK 3-state enable input
      .USRDONEO(1'b1),                  // 1-bit input: User DONE pin output control
      .USRDONETS(1'b1)                  // 1-bit input: User DONE 3-state enable output
   );
   
// reg [5:0]     cnt_clk=0;
// wire      add_cnt_clk;
// wire      end_cnt_clk;
// always@(posedge sys_clk or negedge rst_n)begin
    // if(rst_n==1'b0)begin
        // cnt_clk<=0;
    // end
    // else if(add_cnt_clk)begin
        // if(end_cnt_clk)
            // cnt_clk<=0;
        // else
            // cnt_clk<=cnt_clk+1;
    // end 
// end 
// assign add_cnt_clk = 1;
// assign end_cnt_clk = add_cnt_clk && cnt_clk == 2-1;

// reg clk;//100MHz
// always@(posedge sys_clk)begin
    // if(cnt_clk<1)begin
        // clk<=1'b1;
    // end
    // else begin
        // clk<=1'b0;
    // end 
// end   

// wire clk;
// assign clk=sys_clk;

// wire clk_out1;
// wire clk_out2;
  // clk_wiz_0 clk_wiz_0_uut
   // (

    // .clk_out1(clk_out1),     // output clk_out1
    // .clk_out2(clk_out2),     // output clk_out2
    // .reset(~rst_n), // input reset
    // .locked(locked),       // output locked
    // .clk_in1(sys_clk));      // input clk_in1

// wire clk;
// assign clk=clk_out2 & locked;


//===========================vio=========================================
// wire earse_start_vio;
// wire write_start_vio;
// wire read_start_vio;
// vio_0 uut_vio_0 (
  // .clk(clk),                    
  // .probe_out0(earse_start_vio), 
  // .probe_out1(write_start_vio),
  // .probe_out2(read_start_vio)  
// ); 

// reg earse_start_r1;
// always@(posedge clk)begin
// earse_start_r1<=earse_start_vio;
// end

// wire   earse_edge;
// assign earse_edge= earse_start_r1 & ~earse_start_vio;

// reg write_start_r1;
// always@(posedge clk)begin
// write_start_r1<=write_start_vio;
// end

// wire   write_edge;
// assign write_edge= write_start_r1 & ~write_start_vio;

// reg read_start_r1;
// always@(posedge clk)begin
// read_start_r1<=read_start_vio;
// end

// wire   read_edge;
// assign read_edge= read_start_r1 & ~read_start_vio;    
//===========================test=========================================                             
// reg         I_fl_data_en ;        
// wire        end_cnt0;

// always @(posedge clk or negedge rst_n)begin 
    // if(!rst_n)begin
        // I_fl_data_en <= 0;
    // end
        // else if(write_start) begin //仿真
        // else if(write_edge)begin     //实际
        // I_fl_data_en <= 1;
    // end 
    // else if(end_cnt0) begin
        // I_fl_data_en <= 0;
    // end     
// end 

// reg [15:0] cnt0;
// wire      add_cnt0;

// always@(posedge clk or negedge rst_n)begin
    // if(rst_n==1'b0)begin
        // cnt0<='d0;
    // end
    // else if(add_cnt0)begin
        // if(end_cnt0)
            // cnt0<=0;
        // else
            // cnt0<=cnt0+1;
    // end 
// end 
// assign add_cnt0 = I_fl_data_en;
// assign end_cnt0 = add_cnt0 && cnt0 == 1024-1;//256-1;

//

 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   wr_rd_state_spi_out ;
 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   dout                ;//读取数据
 (*mark_debug="true", dont_touch="true"*)wire                dout_vld            ;//数据有效
 (*mark_debug="true", dont_touch="true"*)wire                spi_vld             ;
 (*mark_debug="true", dont_touch="true"*)wire                en                  ;
 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   spi_dout            ;//读取数据进入fifo                   
 (*mark_debug="true", dont_touch="true"*)wire                spi_one_byte_done   ; 
 



 wr_control u_wr_control( 
     /*input                  */.clk          (clk              ),
     /*input                  */.rst_n         (rst_n            ),
     
     // `ifdef simulation
         // `elsif
                            .erase_start  (erase_start),// erase_start 仿真
                            .write_start  (write_start),//write_start

                            // .erase_start  (earse_edge  ),//erase_start 调试
                            // .write_start  (write_edge  ),//write_start
                            
                            .erase_completed(erase_completed),
                            .four_pp_completed(four_pp_completed),
                            
                            .cmd_reply_clear_finish(cmd_reply_clear_finish),
                            
                            .read_start(read_edge),
    // `endif    
    
     /*input               */.spi_one_byte_done (spi_one_byte_done          ),
     /*input               */.din_vld   ( fl_data_en   ),//   (I_fl_data_en),//(rx_byte_vld      ),//
     /*input        [7:0]  */.din       ( fl_data),//   (cnt0[7:0]   ),//(rx_byte          ),//

     
     /*input        [7:0]  */.read_data       (spi_dout         ),
     
     /*output    reg [7:0] */.wr_rd_state_spi_out  (wr_rd_state_spi_out           ),
     /*output      [7:0]   */.read_flash_data_fifo_out  (dout             ),
     /*output              */.read_flash_data_fifo_out_vld  (dout_vld         ),
     /*output              */.en           (en               ),
     /*output              */.spi_vld      (spi_vld          ) 
 
 );
 
  
 spi_master u_spi_master( 
     /*input               */.clk        (clk            ),
     /*input               */.rst_n      (rst_n          ),
     /*input               */.en         (en             ),
     /*input       [1:0]   */.spi_mode   (2'b00          ),
     /*input               */.spi_vld    (spi_vld        ),
     /*input        [7:0]  */.spi_din    (wr_rd_state_spi_out),//写入数据
     /*input               */.miso       (miso           ),
     /*output    reg       */.sck        (sck            ),
     /*output  reg         */.cs_n       (cs_n           ),
     /*output              */.mosi       (mosi           ),
     /*output  reg [7:0]   */.spi_dout   (spi_dout       ),//读取数据
     /*output  reg         */.busy       (         ),
     /*output              */.spi_one_byte_done  (spi_one_byte_done        ) 
 );                                 
 



endmodule 
flash_top

 

`include "param.v"
module wr_control( 
    input                  clk        ,
    input                  rst_n      ,

    input           erase_start,
(*mark_debug="true", dont_touch="true"*)output   erase_completed,
    
    input           write_start,
(*mark_debug="true", dont_touch="true"*)output  reg four_pp_completed,

    input cmd_reply_clear_finish,
    
    input             read_start,

    input                 spi_one_byte_done     ,
    input        [7:0]     din          ,
    input                  din_vld      ,
    
    input        [7:0]      read_data       ,
    
    
    output    reg [7:0]     wr_rd_state_spi_out      ,
    output      [7:0]     read_flash_data_fifo_out        ,
    output                read_flash_data_fifo_out_vld    ,
    output                en          ,
    output  reg           spi_vld      

);                                 
    //参数定义    
    parameter DATA_WIDTH   = 8'd12;//8'd15;
    parameter DATA_LENGTH  = 1024;//'d10240;    
                        
    //中间信号定义    
    reg                 flag            ;
    reg                 flag0           ;

    reg                 en_r            ;
    wire                en_dge_n        ;
    reg                 read_data_vld        ;



//例化中间信号
    wire read_id       ;
    wire read_rdsr     ;
    wire read_data_flag;
    
    wire read_only_data_flag;
    wire busy_read;

    wire                data_req        ;
    wire    [7:0]       write_state_spi_out           ;
    wire    [7:0]       read_state_spi_out           ;
    wire    [1:0]       en_             ;
    wire                read_spi_one_byte_vld      ;
    wire                write_done      ;
    wire                read_done       ;


    wire                rxf_rdreq       ;
    wire                rxf_wrreq       ;
    wire                rxf_empty       ;
    wire                rxf_full        ;
    wire    [7:0]       rxf_dout        ;
    // wire    [11:0]       rxf_usedw       ; 
    wire    [DATA_WIDTH-1:0]       rxf_usedw       ; 


    wire                txf_rdreq       ;
    wire                txf_wrreq       ;
(*mark_debug="true", dont_touch="true"*)    wire                txf_empty       ;
    wire                txf_full        ;
// (*mark_debug="true", dont_touch="true"*)    wire    [11:0]       txf_usedw    ;   
    wire    [DATA_WIDTH-1:0]       txf_usedw    ;            

reg [7:0] din_fifo;
reg  din_fifo_vld; 

    //标志信号
    //flag 1: uart 发送数据 0:停止发送数据
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <= 0;
        end 
        else if(txf_usedw >= DATA_LENGTH-1)begin 
        // else if(txf_usedw >='d1023)begin          
            flag <= 1;
        end 
        else if(txf_empty)begin 
            flag <= 0;
        end 
    end

    //flag0 0:fish读模块输出数据(命令、地址)1:fish写模块发送数据(命令、地址、写入数据)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag0 <= 0;
        end 
        else if(en_[0])begin 
            flag0 <= 1;
        end 
        else if(en_[1])begin 
            flag0 <= 0;
        end 
    end

    //en_r 打拍 检测en信号边沿
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            en_r <= 0;
        end 
        else begin 
            en_r <= en;
        end 
    end

    assign en_dge_n = !en & en_r;
    
    //read_data_vld
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_data_vld <= 0;
        end 
        else begin 
            read_data_vld <= read_spi_one_byte_vld;//读出数据有效
        end 
    end

    //输出
    //wr_rd_state_spi_out  数据输出给spi_master模块
    always @(*)begin 
         if(flag0)begin
            wr_rd_state_spi_out = write_state_spi_out;
         end 
         else if(flag0 == 0)begin 
            wr_rd_state_spi_out = read_state_spi_out; 
         end 
         else begin 
            wr_rd_state_spi_out = wr_rd_state_spi_out;
         end 
    end
    
    //en
    assign en = en_[0] | en_[1];

    //spi_vld
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            spi_vld <= 0;
        end 
        else if(en_dge_n)begin 
            spi_vld <= 1;
        end 
        else if(write_done | read_done)begin 
           spi_vld <= 0; 
        end 
        else begin
            spi_vld <= spi_vld; 
        end
    end


flash_write u_flash_write
( 
    /*input                 */.clk          (clk            ),
    /*input                 */.rst_n        (rst_n          ),
    
    /*input                 */.erase_start  (erase_start),
    .erase_completed(erase_completed),
    
    .cmd_reply_clear_finish(cmd_reply_clear_finish),
    
    /*input                 */.write_start  (write_start),
                              
                              .read_id      (read_id    ),
                              .read_rdsr    (read_rdsr  ),
                              .read_data_flag(read_data_flag),
                              
    /*input                 */.read_data    (read_data),
    /*input                 */.read_data_vld(read_data_vld),
    /*input                 */.read_done    (read_done),
                                .busy_read(busy_read),
    
    /*input              */.spi_one_byte_done (spi_one_byte_done   ),//spi一个字节数据传输完成
    /*input     [7:0]    */.din         (rxf_dout       ),
    /*output             */.data_req    (data_req       ),           //写入flash数据请求
    /*output             */.en          (en_[0]         ), 
    /*output             */.write_done        (write_done     ),
    
    
    /*output reg [7:0]   */.write_state_spi_out        (write_state_spi_out          ) //给spi配置   

);

flash_read u_flash_read
( 
    /*input                  */.clk     (clk            ),
    /*input                  */.rst_n   (rst_n          ),
    
    .read_id      (read_id    ),
    .read_rdsr    (read_rdsr  ),    
    .read_data_flag(read_data_flag | read_start),
    
    .read_only_data_flag(read_only_data_flag),
    .busy_read(busy_read),
    /*input               */.spi_one_byte_done  (spi_one_byte_done  ),
    /*output              */.en         (en_[1]         ),
    /*output              */.read_done       (read_done      ),
    /*output              */.read_spi_one_byte_vld (read_spi_one_byte_vld     ),
    /*output  reg [7:0]   */.read_state_spi_out       (read_state_spi_out          )//给spi配置
);    

    
//fifo例化
// rx_fifo    rx_fifo_inst (
    // .aclr  ( !rst_n     ),
    // .clock ( clk        ),
    // .data  ( din        ),                                          //uart输入数据
    // .rdreq ( rxf_rdreq  ),
    // .wrreq ( rxf_wrreq  ),
    // .empty ( rxf_empty  ),
    // .full  ( rxf_full   ),
    // .q     ( rxf_dout   ),
    // .usedw ( rxf_usedw  )
    // );

flash_fifo rx_fifo (
  .clk(clk),                // input wire clk
  .srst(!rst_n),              // input wire srst
  // .din(din),                // input wire [7 : 0] din
  .din(din_fifo),
  .wr_en(rxf_wrreq),            // input wire wr_en
  .rd_en(rxf_rdreq),            // input wire rd_en
  .dout(rxf_dout),              // output wire [7 : 0] dout
  .full(rxf_full),              // output wire full
  .empty(rxf_empty),            // output wire empty
  .data_count(rxf_usedw)  // output wire [8 : 0] data_count
);

    // assign rxf_wrreq = !rxf_full && din_vld;
    assign rxf_wrreq = !rxf_full && din_fifo_vld;
    assign rxf_rdreq = !rxf_empty && data_req;

// fifo_tx    fifo_tx_inst (
    // .aclr  ( !rst_n     ),
    // .clock ( clk        ),
    // .data  ( read_data      ),//din_m从flash中读取数据
    // .rdreq ( txf_rdreq  ),
    // .wrreq ( txf_wrreq  ),
    // .empty ( txf_empty  ),
    // .full  ( txf_full   ),
    // .q     ( read_flash_data_fifo_out ),                                           //uart输出数据
    // .usedw ( txf_usedw  )
    // );

flash_fifo fifo_tx (
  .clk(clk),                // input wire clk
  .srst(!rst_n),              // input wire srst
  .din  (read_data  ),                // input wire [7 : 0] din
  .wr_en(txf_wrreq),            // input wire wr_en
  .rd_en(txf_rdreq),            // input wire rd_en
  .dout(read_flash_data_fifo_out),              // output wire [7 : 0] dout
  .full(txf_full),              // output wire full
  .empty(txf_empty),            // output wire empty
  .data_count(txf_usedw)  // output wire [11 : 0] data_count
);

    assign txf_wrreq = !txf_full && read_data_vld && read_only_data_flag;//从flash中读取数据  data_vld数据有效
    assign txf_rdreq = flag && (!txf_empty) ;//&& (!busy_t);
    assign read_flash_data_fifo_out_vld = txf_rdreq;                                    //uart数据输出有效
 
(*mark_debug="true", dont_touch="true"*)reg [8:0]     cnt_fifo;
wire      add_cnt_fifo;
wire      end_cnt_fifo;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_fifo<=0;
    end
    else if(add_cnt_fifo)begin
        if(end_cnt_fifo)
            cnt_fifo<=0;
        else
            cnt_fifo<=cnt_fifo+1;
    end 
end 
assign add_cnt_fifo = txf_wrreq;
assign end_cnt_fifo = add_cnt_fifo && cnt_fifo == 256-1;//一页写字节

//--------------------比较-----------------------

(*mark_debug="true", dont_touch="true"*)wire [7 : 0] original_dout;
wire compare_fifo_full;
(*mark_debug="true", dont_touch="true"*)wire compare_fifo_empty;
// (*mark_debug="true", dont_touch="true"*)wire [11 : 0] compare_fifo_usedw;
(*mark_debug="true", dont_touch="true"*)wire [DATA_WIDTH-1:0] compare_fifo_usedw;
flash_fifo compare_fifo (
  .clk(clk),                // input wire clk
  .srst(!rst_n),              // input wire srst
  // .din(din),                // input wire [7 : 0] din
  .din(din_fifo), 
  .wr_en(rxf_wrreq),            // input wire wr_en
  .rd_en(txf_rdreq),            // input wire rd_en
  .dout(original_dout),              // output wire [7 : 0] dout
  .full(compare_fifo_full),              // output wire full
  .empty(compare_fifo_empty),            // output wire empty
  .data_count(compare_fifo_usedw)  // output wire [11 : 0] data_count
);


// (*mark_debug="true", dont_touch="true"*)reg [11:0]     cnt_compare;
(*mark_debug="true", dont_touch="true"*)reg [DATA_WIDTH-1:0]     cnt_compare;
wire      add_cnt_compare;
wire      end_cnt_compare;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_compare<=0;
    end
    else if(add_cnt_compare)begin
        if(end_cnt_compare)
            cnt_compare<=0;
        else
            cnt_compare<=cnt_compare+1;
    end 
end 
assign add_cnt_compare = txf_rdreq&original_dout==read_flash_data_fifo_out/* &(~compare_fifo_empty) */;
assign end_cnt_compare = add_cnt_compare && cnt_compare == DATA_LENGTH-1;//10240-1;//回读比较字节
// assign end_cnt_compare = add_cnt_compare && cnt_compare == 1024-1;

// (*mark_debug="true", dont_touch="true"*)wire four_pp_completed;
// assign  four_pp_completed=end_cnt_compare;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        four_pp_completed <=1'b0;
    end 
    else if(end_cnt_compare)begin 
        four_pp_completed <= 1'b1;
    end 
    else if(cmd_reply_clear_finish)begin 
       four_pp_completed <= 1'b0; 
    end 
end


//不够1024字节后面都写ff
reg din_vld_r;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        din_vld_r<=0;
    end
    else begin
        din_vld_r<=din_vld;
    end  
end

wire   din_vld_pos;
assign din_vld_pos = din_vld & ~din_vld_r;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        din_fifo<='hff;
    end
    else if(din_vld)begin
        din_fifo<=din;
    end 
    else begin
        din_fifo<='hff;    
    end 
end

// reg  din_fifo_vld;
 wire      end_cnt_din;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        din_fifo_vld<=1'b0;
    end
    else if(din_vld_pos)begin
        din_fifo_vld<=1'b1;
    end 
    else if(end_cnt_din)begin
        din_fifo_vld<=0;    
    end 
end

reg [15:0]     cnt_din;
wire       add_cnt_din;
 // wire      end_cnt_din;
always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt_din<='d0;
    end
    else if(add_cnt_din)begin
        if(end_cnt_din)
            cnt_din<=0;
        else
            cnt_din<=cnt_din+1;
    end 
end 
assign add_cnt_din = din_fifo_vld;
assign end_cnt_din = add_cnt_din && cnt_din == 1024-1;//256-1;//一页输入字节

//收包统计个数

(*mark_debug="true", dont_touch="true"*)reg [15:0]     cnt_udp;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_udp<=0;
    end
    else if(din_vld_pos)begin
        cnt_udp<=cnt_udp+1'b1;
    end 
    else begin
        cnt_udp<=cnt_udp;    
    end 
end

endmodule
wr_control

 

`include "param.v"
module flash_write( 
    input                clk            ,
    input                rst_n        ,
    
    input erase_start,
    output    reg erase_completed,
    input cmd_reply_clear_finish,
    
    input write_start,
    
     output     reg read_id,
 (*mark_debug="true", dont_touch="true"*)    output  reg read_rdsr,
 (*mark_debug="true", dont_touch="true"*)    output  reg read_data_flag,
        
 (*mark_debug="true", dont_touch="true"*)    input [7:0] read_data,
 (*mark_debug="true", dont_touch="true"*)    input       read_data_vld,
 (*mark_debug="true", dont_touch="true"*)    input read_done,
    input busy_read,
    
    input               spi_one_byte_done      ,
    input       [7:0]   din         ,
    output              data_req    ,           //写入flash数据请求
    output              en          ,    
    output              write_done        ,  

    output  reg [7:0]   write_state_spi_out    

);                                 
    //参数定义    
    parameter M_IDLE =8'b0000_0001,
              M_RDID =8'b0000_0010,
              M_WREN =8'b0000_0100,
              M_SE   =8'b0000_1000, 
              M_RDSR =8'b0001_0000,
              M_WAIT =8'b0010_0000,
              M_RDDA =8'b0100_0000,
              M_PP   =8'b1000_0000;      

    parameter S_IDLE  = 5'b00001, 
              S_CMD   = 5'b00010, 
              S_ADDR  = 5'b00100, 
              S_DATA  = 5'b01000, 
              S_DELAY = 5'b10000;    
          
    parameter INIT_ADDR   = 24'h80_0000;                        
    //中间信号定义    
   reg     [7:0]               m_state_c           ;
    reg     [7:0]               m_state_n           ;

  (*mark_debug="true", dont_touch="true"*)   reg     [4:0]               s_state_c           ;
    reg     [4:0]               s_state_n           ;

    reg     [7:0]               cnt_100ns           ;
    wire                        add_cnt_100ns       ;
    wire                        end_cnt_100ns       ;

    reg     [18:0]              cnt_pp              ;
    wire                        add_cnt_pp          ;
    wire                        end_cnt_pp          ;
    reg                         end_cnt_pp_r        ;

    reg     [10:0]              cnt_se              ;
    wire                        add_cnt_se          ;
    wire                        end_cnt_se          ;
    reg                         end_cnt_se_r        ;    

    reg     [8:0]               cnt_byte            ;
    wire                        add_cnt_byte        ;
    wire                        end_cnt_byte        ;

    reg     [8:0]               byte_num            ;


(*mark_debug="true", dont_touch="true"*)    reg     [23:0]              addr_wr             ;
(*mark_debug="true", dont_touch="true"*)    reg     [1:0]               flag                ;

    //
    wire                        m_idle2m_rdid       ;
    wire                         m_rdid2m_wren       ;
    wire                        m_rdid2m_idle       ;
    wire                        m_wren2m_se         ;
    wire                        m_wren2m_pp         ;
    wire                         m_se2m_rdsr         ;
    wire                         m_rdsr2m_rdda       ;
    wire                         m_rdsr2m_idle       ;
    
    wire                        m_rdsr2m_wren       ;
    wire                        m_rdsr2m_wait       ;
    wire                        m_wait2m_rdsr       ;
    wire                         m_rdda2m_wren         ;
    wire                         m_rdda2m_idle        ;
    wire                         m_pp2m_rdsr            ;

    //
    wire                        s_idle2s_cmd        ;
    wire                        s_cmd2s_addr        ;
    wire                        s_cmd2s_data        ;
    wire                        s_cmd2s_delay       ;
    wire                        s_addr2s_data       ;
    wire                        s_addr2s_delay      ;
    wire                        s_data2s_idle       ;
    wire                        s_data2s_delay      ;
    wire                        s_delay2s_idle      ;

     reg [2:0] read_id_cnt;
    reg [7:0] first_id ;
    reg [7:0] second_id;
    reg [7:0] third_id ;
    reg  read_done_r1;
    reg  read_done_r2;
    reg  read_done_r3;
    reg [1:0] read_id_flag;
    reg [11:0] erase_cnt;
    reg  rdsr_sta_reg_vld ;
    reg [23:0] wait_time_cnt;
    reg [11:0] pp_cnt;
    
        reg second_erase_start;

    //状态机
    //主状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            m_state_c <= M_IDLE;
        end 
        else begin 
            m_state_c <= m_state_n;
        end 
    end

    always @(*)begin 
         case (m_state_c)
            M_IDLE: begin if(m_idle2m_rdid)begin
                m_state_n = M_RDID;
            end
            else begin
                m_state_n = m_state_c;
            end
            end 
            M_RDID: begin if(m_rdid2m_wren)begin
                m_state_n = M_WREN;
            end 
            else if(m_rdid2m_idle)begin
                m_state_n = M_IDLE;
            end                
            else begin
                m_state_n = m_state_c;
            end            
            end
            M_WREN: begin if(m_wren2m_se)begin
                m_state_n = M_SE;
            end    
            else if(m_wren2m_pp)begin
                m_state_n = M_PP;
            end
            else begin
                m_state_n = m_state_c;
            end
            end
            M_SE: begin if(m_se2m_rdsr)begin
                m_state_n = M_RDSR;
            end
            else begin
                m_state_n = m_state_c;
            end    
            end
            M_RDSR: begin if(m_rdsr2m_rdda)begin
                m_state_n = M_RDDA;
            end
            else if (m_rdsr2m_idle) begin
                m_state_n = M_IDLE;
            end            
            else if (m_rdsr2m_wren) begin
                m_state_n = M_WREN;
            end
            else if (m_rdsr2m_wait) begin
                m_state_n = M_WAIT;
            end            
            else begin
                m_state_n = m_state_c;
            end    
            end
            M_WAIT:begin if(m_wait2m_rdsr)begin
                    m_state_n = M_RDSR;
            end 
            else begin
                    m_state_n = m_state_c;
            end 
            end 
            M_RDDA:begin if(m_rdda2m_wren)begin
                    m_state_n = M_WREN;
            end 
            else if(m_rdda2m_idle)begin
                    m_state_n = M_IDLE;
            end 
            else begin
                    m_state_n = m_state_c;
            end             
            end 
            M_PP: begin if (m_pp2m_rdsr) begin
                m_state_n = M_RDSR;
            end        
            else begin
                m_state_n = m_state_c;
            end    
            end
             default: m_state_n = m_state_c;
         endcase
    end

    //从状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            s_state_c <= S_IDLE;
        end 
        else begin 
            s_state_c <= s_state_n;
        end 
    end

    always @(*)begin 
         case (s_state_c)
            S_IDLE  : begin if (s_idle2s_cmd) begin
                s_state_n = S_CMD;
            end
            else begin
               s_state_n = s_state_c; 
            end    
            end
            S_CMD   : begin if (s_cmd2s_addr) begin
                s_state_n = S_ADDR;
            end
            // else if (s_cmd2s_data) begin
                // s_state_n = S_DATA;
            // end
            else if (s_cmd2s_delay) begin
                s_state_n = S_DELAY;
            end
            else begin
                s_state_n = s_state_c;
            end    
            end
            S_ADDR  : begin if (s_addr2s_data) begin
                s_state_n = S_DATA;
            end
            else if(s_addr2s_delay) begin
                s_state_n = S_DELAY;
            end
            else begin
                s_state_n = s_state_c;
            end   
            end
            S_DATA  : begin 
            
            // if (s_data2s_idle) begin
                // s_state_n = S_IDLE;
            // end
            // else 
            
            if(s_data2s_delay)begin
                s_state_n = S_DELAY;
            end
            else begin
                s_state_n = s_state_c;
            end    
            end
            S_DELAY : begin if(s_delay2s_idle)begin
                s_state_n = S_IDLE;
            end
            else begin
                s_state_n = s_state_c;
            end    
            end
             default: s_state_n = s_state_c;
         endcase
    end
 
    //主状态机转换条件
    assign m_idle2m_rdid   = m_state_c == M_IDLE && (erase_start || write_start ||second_erase_start);
    assign m_rdid2m_wren   = m_state_c == M_RDID && read_id_flag==2'b11;
    assign m_rdid2m_idle   = m_state_c == M_RDID && read_id_flag==2'b10;
    assign m_wren2m_se     = m_state_c == M_WREN && s_delay2s_idle && flag == 2'b01;    
    assign m_wren2m_pp     = m_state_c == M_WREN && s_delay2s_idle && flag == 2'b10;    
    assign m_se2m_rdsr     = m_state_c == M_SE   && s_delay2s_idle;
    assign m_rdsr2m_rdda   = m_state_c == M_RDSR && /* s_data2s_idle && */ rdsr_sta_reg_vld && flag == 2'b10;
    assign m_rdsr2m_idle   = m_state_c == M_RDSR && /* s_data2s_idle && */ rdsr_sta_reg_vld && erase_cnt==12'd60 &&flag == 2'b01;
    assign m_rdsr2m_wren   = m_state_c == M_RDSR && /* s_data2s_idle && */ rdsr_sta_reg_vld && erase_cnt <12'd60 &&flag == 2'b01;
    assign m_rdsr2m_wait   = m_state_c == M_RDSR && /* s_data2s_idle */ read_done_r1 && read_data_vld && read_data[1:0] == 2'b11;
    assign m_wait2m_rdsr   = m_state_c == M_WAIT && ((flag == 2'b01 && wait_time_cnt== 24'd250_0000)
                                                     |(flag == 2'b10 && wait_time_cnt== 24'd150_000 ));
    assign m_rdda2m_wren   = m_state_c == M_RDDA && /* read_done */ (~read_data_flag)&&read_done_r3 &&flag == 2'b10 && pp_cnt< 12'd4;
    assign m_rdda2m_idle   = m_state_c == M_RDDA && /* read_done */ (~read_data_flag)&&read_done_r3 &&flag == 2'b10 && pp_cnt==12'd4;//1024字节4页
    assign m_pp2m_rdsr     = m_state_c == M_PP   && s_delay2s_idle;
    
     
    //从状态机转换条件
    assign s_idle2s_cmd   = s_state_c == S_IDLE  && (!(m_state_c == M_IDLE)) && (!(m_state_c == M_RDSR)) && (!(m_state_c == M_RDID)) && (!(m_state_c == M_RDDA)) ;
    assign s_cmd2s_addr   = s_state_c == S_CMD   && end_cnt_byte && (m_state_c == M_SE | m_state_c == M_PP);
    assign s_cmd2s_data   = s_state_c == S_CMD   && end_cnt_byte && (m_state_c == M_RDSR | m_state_c == M_RDID);
    assign s_addr2s_data  = s_state_c == S_ADDR  && end_cnt_byte && m_state_c == M_PP;
    assign s_addr2s_delay = s_state_c == S_ADDR  && end_cnt_byte && m_state_c == M_SE;
    assign s_data2s_idle  = s_state_c == S_DATA  && read_done_r3 && (m_state_c == M_RDSR|| m_state_c == M_RDID);
    
    assign s_data2s_delay = s_state_c == S_DATA  && end_cnt_byte && (m_state_c == M_PP);
    assign s_cmd2s_delay  = s_state_c == S_CMD   && end_cnt_byte && m_state_c == M_WREN;
    assign s_delay2s_idle = s_state_c == S_DELAY && ((( (m_state_c == M_SE)?end_cnt_se_r : end_cnt_pp_r)
                     && (m_state_c == M_SE || m_state_c == M_PP)) || (end_cnt_100ns && m_state_c == M_WREN));

    //计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_100ns <= 0;
        end 
        else if(add_cnt_100ns)begin 
                if(end_cnt_100ns)begin 
                    cnt_100ns <= 0;
                end
                else begin 
                    cnt_100ns <= cnt_100ns + 1;
                end 
        end
       else  begin
           cnt_100ns <= cnt_100ns;
        end
    end 
    
    assign add_cnt_100ns = s_state_c == S_DELAY && m_state_c == M_WREN;
    assign end_cnt_100ns = add_cnt_100ns && cnt_100ns == 30;

    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 0;
        end 
        else if(add_cnt_byte)begin 
                if(end_cnt_byte)begin 
                    cnt_byte <= 0;
                end
                else begin 
                    cnt_byte <= cnt_byte + 1;
                end 
        end
       else  begin
           cnt_byte <= cnt_byte;
        end
    end 
    
    assign add_cnt_byte = spi_one_byte_done && (s_state_c == S_CMD | s_state_c == S_ADDR | s_state_c == S_DATA ) && ~busy_read;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_num-1; 


    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_pp <= 0;
        end 
        else if(add_cnt_pp)begin 
                if(end_cnt_pp | s_delay2s_idle)begin 
                    cnt_pp <= 0;
                end
                else begin 
                    cnt_pp <= cnt_pp + 1;
                end 
        end
       else  begin
           cnt_pp <= cnt_pp;
        end
    end 
    
    assign add_cnt_pp = s_state_c == S_DELAY && (m_state_c == M_PP || m_state_c == M_SE);
    assign end_cnt_pp = add_cnt_pp && cnt_pp == `DELAY_5MS-1;


    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_se <= 0;
        end 
        else if(add_cnt_se)begin 
                if(end_cnt_se)begin 
                    cnt_se <= 0;
                end
                else begin 
                    cnt_se <= cnt_se + 1;
                end 
        end
       else  begin
           cnt_se <= cnt_se;
        end
    end 
    
    assign add_cnt_se= s_state_c == S_DELAY && end_cnt_pp && m_state_c == M_SE;
    assign end_cnt_se = add_cnt_se && cnt_se == 2-1;

    //flag
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <=2'b00;
        end 
        else if(erase_start)begin
            flag <=2'b01;
        end 
        else if(write_start)begin
            flag <=2'b10;
        end                 
    end


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
        end_cnt_pp_r <= 0;
        end_cnt_se_r <= 0;
        end 
        else begin 
            end_cnt_pp_r <= end_cnt_pp;
            end_cnt_se_r <= end_cnt_se;
        end 
    end

    //byte_num
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            byte_num <= 0;
        end 
        else if(s_state_c == S_CMD)begin 
            byte_num <= `CMD_BYTE;
        end 
        else if(s_state_c == S_ADDR)begin 
            byte_num <= `ADDR_BYTE;
        end 
        else if(s_state_c == S_DATA)begin
            byte_num <= `DATA_BYTE;
        end
        else begin
            byte_num <= byte_num;
        end
    end

    reg [23:0] addr_erase;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            addr_erase <= INIT_ADDR;//24'h40_0000;
        end 
        else if(m_rdsr2m_wren & flag == 2'b01)begin 
            addr_erase <= addr_erase +24'h01_0000;
        end 
        else begin 
            addr_erase <= addr_erase;
        end 
    end
    
    reg [23:0] addr_pp;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n /* | m_state_c == M_IDLE */)begin
            addr_pp <=INIT_ADDR;
        end
        // else if(addr_pp ==24'h7a_607b)begin        
            // addr_pp <=INIT_ADDR;
        // end        
        else if(m_pp2m_rdsr & flag == 2'b10)begin 
            addr_pp <= addr_pp +24'h00_0100;
        end 
        else begin 
            addr_pp <= addr_pp;
        end 
    end    

    //addr_wr
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            addr_wr <= INIT_ADDR;
        end 
        else if(flag==2'b01)begin 
            addr_wr <= addr_erase;
        end 
        else if(flag==2'b10 )begin 
            addr_wr <= addr_pp;
        end         
        else begin 
            addr_wr <= addr_wr;
        end 
    end

    //输出
    //write_state_spi_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            write_state_spi_out <= 0;
        end 
        else if(s_state_c == S_CMD)begin 
            if(m_state_c == M_WREN)begin
                write_state_spi_out <= `CMD_WREN;
            end
            else if(m_state_c == M_SE ) begin
                write_state_spi_out <= `CMD_SE;
            end
            else if (m_state_c == M_PP ) begin
                write_state_spi_out <= `CMD_PP;
            end
            else begin
                write_state_spi_out <= write_state_spi_out;
            end
        end 
        else if(s_state_c == S_ADDR)begin 
            write_state_spi_out <=addr_wr[23-cnt_byte*8-:8];
        end 
        else if (s_state_c == S_DATA && m_state_c == M_PP) begin
            write_state_spi_out <=din;
        end
    end

    //data_req en write_done read_data_vld busy
    // wire en_temp;
    assign data_req = m_state_c == M_PP && spi_one_byte_done  && s_state_c == S_DATA; 
    assign /* en_temp */  en  = m_rdid2m_wren |m_rdsr2m_wren |m_wren2m_se |m_wren2m_pp |m_rdda2m_wren;
    assign write_done = s_data2s_delay | s_addr2s_delay | s_cmd2s_delay;

    // reg [7:0] en_r;
    // always @(posedge clk or negedge rst_n)begin 
        // if(!rst_n)begin
            // en_r[7:0] <= 8'h0;
        // end 
        // else begin
            // en_r[7:0] <= {en_r[6:0],en_temp};
        // end 
    // end    

    // assign en=en_r[7];

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_id <= 1'b0;
        end 
        else if(erase_start|write_start|second_erase_start)begin 
            read_id <= 1'b1;
        end
        else begin
            read_id <= 1'b0;
        end 
    end    
    

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            read_id_cnt <= 'd0;
        end 
        else if(m_state_c == M_RDID && read_data_vld)begin 
            read_id_cnt <= read_id_cnt + 1'b1;
        end
        else begin
            read_id_cnt <= read_id_cnt;
        end 
    end    


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            first_id  <= 'd0;
            second_id <= 'd0;
            third_id  <= 'd0;
        end 
        else if(m_state_c == M_RDID && read_data_vld &&read_id_cnt==0)begin 
            first_id  <= read_data;
        end
        else if(m_state_c == M_RDID && read_data_vld &&read_id_cnt==1)begin 
            second_id  <= read_data;
        end        
        else if(m_state_c == M_RDID && read_data_vld &&read_id_cnt==2)begin 
            third_id  <= read_data;
        end
        else begin
            first_id  <= first_id ;
            second_id <= second_id;
            third_id  <= third_id ;        
        end 
    end    


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n )begin
            read_done_r1<=1'b0;
            read_done_r2<=1'b0;
            read_done_r3<=1'b0;
        end 
        else begin 
            read_done_r1<=read_done;
            read_done_r2<=read_done_r1;
            read_done_r3<=read_done_r2;
        end
    end


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            read_id_flag <= 2'b00;
        end 
        else if(read_done_r3 && first_id==`MF_ID && second_id == `ID1 && third_id == `ID2)begin 
            read_id_flag <= 2'b11;
        end
        else if(read_done_r3 && (first_id != `MF_ID || second_id != `ID1 || third_id != `ID2))begin 
            read_id_flag <= 2'b10;//实际
            // read_id_flag <= 2'b11;//仿真
        end
        else if(m_state_c != M_RDID)begin
            read_id_flag <= 2'b00;        
        end 
        else begin
            read_id_flag <= read_id_flag;
        end 
    end        


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            erase_cnt <= 'd0;
        end 
        else if(m_se2m_rdsr)begin 
            erase_cnt <= erase_cnt + 1'b1;
        end
        else begin
            erase_cnt <= erase_cnt;
        end 
    end


    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            rdsr_sta_reg_vld  <= 1'b0;
        end 
        else if(m_state_c == M_RDSR && read_data_vld && read_data[1:0] == 2'b00)begin 
            rdsr_sta_reg_vld  <= 1'b1;
        end
        else if(m_state_c != M_RDSR)begin
            rdsr_sta_reg_vld  <= 1'b0;
        end 
        else begin
            rdsr_sta_reg_vld  <= rdsr_sta_reg_vld ;        
        end 
    end    

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_rdsr <= 1'b0;
        end 
        else if(m_se2m_rdsr|m_wait2m_rdsr|m_pp2m_rdsr)begin 
            read_rdsr <= 1'b1;
        end
        else begin
            read_rdsr <= 1'b0;
        end 
    end
    

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wait_time_cnt <= 24'd0;
        end 
        else if(m_state_c == M_WAIT)begin 
            wait_time_cnt <= wait_time_cnt+1'b1;
        end
        else if(m_state_c != M_WAIT)begin
            wait_time_cnt <= 24'd0;        
        end 
        else begin
            wait_time_cnt <= wait_time_cnt;
        end 
    end    
    
    

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n | m_state_c == M_IDLE)begin
            pp_cnt <= 'd0;
        end 
        else if(m_pp2m_rdsr)begin 
            pp_cnt <= pp_cnt + 1'b1;
        end
        else begin
            pp_cnt <= pp_cnt;
        end 
    end    

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_data_flag <= 1'b0;
        end 
        else if(m_rdsr2m_rdda)begin 
            read_data_flag <= 1'b1;
        end
        else begin
            read_data_flag <= 1'b0;
        end 
    end

    // reg erase_completed;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            erase_completed <= 1'b0;
        end 
        else if(m_rdsr2m_idle)begin 
            erase_completed <= 1'b1;
        end
        else if(cmd_reply_clear_finish)begin
            erase_completed <= 1'b0;
        end 
    end

//
    // reg second_erase_start;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            second_erase_start <= 1'b0;
        end 
        else if(m_rdid2m_idle)begin 
            second_erase_start <= 1'b1;
        end
        else if(m_idle2m_rdid)begin
            second_erase_start <= 1'b0;
        end 
    end
    
endmodule
flash_write

 

`include "param.v"
module flash_read( 
    input                clk            ,
    input                rst_n        ,
    
    input read_id,
    input read_rdsr,
    input read_data_flag,
    
(*mark_debug="true", dont_touch="true"*)    output     reg read_only_data_flag,
    output  wire busy_read,
    
    input               spi_one_byte_done      ,
    output              en          ,
    output              read_done        ,
    output              read_spi_one_byte_vld  ,
    output  reg [7:0]   read_state_spi_out    
);                                 
    //参数定义        
    parameter IDLE      =9'b00000_0001,
              RDID_CMD  =9'b00000_0010, 
              RDID_DATA =9'b00000_0100,
              RDSR_CMD  =9'b00000_1000,
              RDSR_DATA =9'b00001_0000,
              RDAD_CMD  =9'b00010_0000, 
              RDAD_ADDR =9'b00100_0000, 
              RDAD_DATA =9'b01000_0000, 
              RD_DONE   =9'b10000_0000;
              
    parameter INIT_ADDR   = 24'h80_0000;
                        
    //中间信号定义    
(*mark_debug="true", dont_touch="true"*)    reg     [8:0]               state_c                 ;
    reg     [8:0]               state_n                 ;

    reg        [8:0]               cnt_byte                ;
    wire                        add_cnt_byte            ;
    wire                        end_cnt_byte            ;

    reg     [8:0]               byte_num                ;

(*mark_debug="true", dont_touch="true"*)    reg     [23:0]              addr_rd                 ;

    wire                        idle2rdid_cmd           ;
    wire                        idle2rdsr_cmd             ;
    wire                        idle2rdad_cmd           ;
    wire                        rdid_cmd2rdid_data      ;
    wire                         rdsr_cmd2rdsr_data      ;
    wire                        rdad_cmd2rdad_addr      ;
    wire                        rdad_addr2rdad_data     ;
    wire                        rdid_data2rd_done       ;
    wire                         rdsr_data2rd_done       ;
    wire                        rdad_data2rd_done       ;
    wire                        rd_done2idle            ;


    //状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end

    always @(*)begin 
         case (state_c)
        IDLE      : begin if (idle2rdid_cmd) begin
            state_n <= RDID_CMD;
        end
        else if(idle2rdsr_cmd) begin
            state_n <= RDSR_CMD;
        end        
        else if(idle2rdad_cmd) begin
            state_n <= RDAD_CMD;
        end
        else begin
            state_n <= state_c;
        end  
        end
        RDID_CMD  : begin if(rdid_cmd2rdid_data)begin
            state_n <= RDID_DATA;
        end
        else begin
            state_n <= state_c;
        end 
        end
        RDID_DATA : begin if (rdid_data2rd_done) begin
            state_n <= RD_DONE;
        end
        else begin
            state_n <= state_c;
        end
        end 
        RDSR_CMD  : begin if(rdsr_cmd2rdsr_data)begin
            state_n <= RDSR_DATA;
        end
        else begin
            state_n <= state_c;
        end 
        end        
        RDSR_DATA : begin if (rdsr_data2rd_done) begin
            state_n <= RD_DONE;
        end
        else begin
            state_n <= state_c;
        end        
        end
        RDAD_CMD  : begin if (rdad_cmd2rdad_addr) begin
            state_n <= RDAD_ADDR;
        end
        else begin
            state_n <= state_c;
        end
        end
        RDAD_ADDR : begin if (rdad_addr2rdad_data ) begin
            state_n <= RDAD_DATA;
        end
        else begin
            state_n <= state_c;
        end  
        end
        RDAD_DATA : begin if(rdad_data2rd_done)begin
            state_n <= RD_DONE;
        end
        else begin
            state_n <= state_c;
        end
        end
        RD_DONE   : begin if(rd_done2idle)begin
            state_n <= IDLE;
        end
        else begin
            state_n <= state_c;
        end
        end
             default: state_n <= state_c;
         endcase
    end

    assign idle2rdid_cmd       = state_c == IDLE    && read_id   ; 
    assign idle2rdsr_cmd       = state_c == IDLE    && read_rdsr ; 
    assign idle2rdad_cmd       = state_c == IDLE    && read_data_flag ;     
    assign rdid_cmd2rdid_data  = state_c == RDID_CMD  && end_cnt_byte;  
    assign rdsr_cmd2rdsr_data  = state_c == RDSR_CMD  && end_cnt_byte; 
    assign rdad_cmd2rdad_addr  = state_c == RDAD_CMD  && end_cnt_byte;     
    assign rdad_addr2rdad_data = state_c == RDAD_ADDR && end_cnt_byte;      
    assign rdid_data2rd_done   = state_c == RDID_DATA && end_cnt_byte;  
    assign rdsr_data2rd_done   = state_c == RDSR_DATA && end_cnt_byte;
    assign rdad_data2rd_done   = state_c == RDAD_DATA && end_cnt_byte; 
    assign rd_done2idle        = state_c == RD_DONE   && 1'b1;

//计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 0;
        end 
        else if(add_cnt_byte)begin 
                if(end_cnt_byte)begin 
                    cnt_byte <= 0;
                end
                else begin 
                    cnt_byte <= cnt_byte + 1;
                end 
        end
       else  begin
           cnt_byte <= cnt_byte;
        end
    end 

    assign add_cnt_byte = (state_c == RDID_CMD | state_c == RDID_DATA |state_c == RDSR_CMD | state_c == RDSR_DATA | state_c == RDAD_CMD | state_c == RDAD_ADDR |state_c == RDAD_DATA) && spi_one_byte_done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_num-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            byte_num <= 0;
        end 
        else if(state_c == RDID_CMD |state_c == RDSR_CMD | state_c == RDAD_CMD )begin 
           byte_num <= `CMD_BYTE; 
        end 
        else if(state_c == RDID_DATA)begin 
            byte_num <= `ID_BYTE; 
        end 
        else if(state_c == RDSR_DATA)begin 
            byte_num <= `RDSR_BYTE; 
        end         
        else  if (state_c == RDAD_ADDR) begin
            byte_num <= `ADDR_BYTE;
        end
        else if(state_c == RDAD_DATA)begin
            byte_num <= `DATA_BYTE;
        end
    end

    //addr_rd
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n /* | addr_rd==24'h00_0400 */)begin
            addr_rd <= INIT_ADDR;
        end 
        else if(rdad_data2rd_done)begin 
            addr_rd <= addr_rd + 24'h00_0100;
        end 
        else begin 
            addr_rd <= addr_rd;
        end 
    end

    //输出 
    //read_state_spi_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_state_spi_out <= 0;
        end 
        else if(state_c == RDID_CMD)begin 
            read_state_spi_out <= `CMD_RDID;
        end 
        else if(state_c == RDSR_CMD)begin 
            read_state_spi_out <= `CMD_RDSR;
        end         
        else if(state_c == RDAD_CMD)begin 
            read_state_spi_out <= `CMD_READ;
        end
        else if(state_c == RDAD_ADDR)begin
            if(cnt_byte == 0)
            begin
               read_state_spi_out <= addr_rd[23:16];
            end
            else if(cnt_byte == 1)
            begin
                read_state_spi_out <= addr_rd[15:8];
            end
            else if(cnt_byte == 2)
            begin
                read_state_spi_out <= addr_rd[7:0];
            end
        end
        else begin
            read_state_spi_out <= read_state_spi_out;
        end 
    end
    
    //en read_done dtat_m_vld
    assign en =(read_id |read_rdsr| read_data_flag) & state_c == IDLE;
    assign read_done = rdid_data2rd_done  | rdsr_data2rd_done| rdad_data2rd_done;
    assign read_spi_one_byte_vld = spi_one_byte_done && (state_c == RDID_DATA | state_c == RDSR_DATA | state_c == RDAD_DATA);
    
    // assign read_only_data_flag=state_c == RDAD_DATA;
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            read_only_data_flag<=1'b0;
        end 
        else if(state_c == RDAD_DATA)begin
            read_only_data_flag<=1'b1;
        end
        else begin
            read_only_data_flag<=1'b0;
        end        
    end         
        
    assign busy_read= !(state_c==IDLE);

endmodule
flash_read

 

 `include "param.v"
 module spi_master( 
     input                clk        ,
     input                rst_n    ,
     input               en      ,
     input       [1:0]   spi_mode,
     input               spi_vld ,
     input        [7:0]    spi_din   ,
     input               miso    ,
     output    reg      sck      ,
     output  reg         cs_n    ,
     output                mosi    ,
     output  reg [7:0]   spi_dout    ,
     output  reg         busy    ,
     output              spi_one_byte_done 
 );                                 
                 
                         
     //中间信号定义        
     
     reg         [4:0]                   cnt_bit             ;
     wire                                add_cnt_bit         ;
     wire                                end_cnt_bit         ;
 
     reg         [8:0]                   cnt_sck             ;
     wire                                add_cnt_sck         ;
     wire                                end_cnt_sck         ;
     reg                                 end_cnt_sck_r       ;
 
 
     //计数器 
     //cnt_sck
     always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
             cnt_sck <= 0;
         end 
         else if(add_cnt_sck)begin 
                 if(end_cnt_sck | en)begin 
                     cnt_sck <= 0;
                 end
                 else begin 
                     cnt_sck <= cnt_sck + 1;
                 end 
         end
        else  begin
            cnt_sck <= cnt_sck;
         end
     end 
     
     assign add_cnt_sck = spi_vld;
     assign end_cnt_sck = add_cnt_sck && cnt_sck == `SCK_PERIOD-1;
 
     //cnt_bit
     always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
             cnt_bit <= 0;
         end 
         else if(add_cnt_bit )begin 
                 if(end_cnt_bit | en)begin 
                     cnt_bit <= 0;
                 end
                 else begin 
                     cnt_bit <= cnt_bit + 1;
                 end 
         end
        else  begin
            cnt_bit <= cnt_bit;
         end
     end 
     
     assign add_cnt_bit =  (cs_n == 0)&& end_cnt_sck;
     assign end_cnt_bit = add_cnt_bit && cnt_bit == 8-1;
 
     //sck
     always @(*)begin
         case (spi_mode)
            0 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
                sck <= 0;
            end
            else if(cnt_sck > `SCK_HALF-1)begin
                 sck <= 1;
            end 
            else begin
               sck <= 0; 
            end  
            end
            1 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
                sck <= 1;
            end
            else if(cnt_sck > `SCK_HALF-1)begin
                 sck <= 0;
            end 
            else begin
                sck <= 0; 
            end 
            end
            2 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1 ))begin
                sck <= 1;
            end
            else if(cnt_sck > `SCK_HALF-1 )begin
                 sck <= 0;
            end  
            else begin
                sck <= 1;
            end
            end
            3 : begin if(spi_vld && (cnt_sck <= `SCK_HALF-1))begin
                sck <= 0;
            end
            else if(cnt_sck > `SCK_HALF-1 )begin
                 sck <= 1;
            end  
            else begin
                sck <= 1;
            end
            end 
             default: sck <= sck;
         endcase
     end
 
     //end_cnt_sck_r
     always @(posedge clk or negedge rst_n)begin 
         if(!rst_n)begin
             end_cnt_sck_r <= 0;
         end 
         else begin 
             end_cnt_sck_r <= end_cnt_sck;
         end 
     end
     
     //输出
     //cs_n
     always @(posedge clk or negedge rst_n)begin 
         if(!rst_n)begin
             cs_n <= 1;
         end 
         else if(en)begin 
            cs_n <= 0; 
         end 
         else if(cs_n == 0 && (!spi_vld) && end_cnt_sck_r)begin 
             cs_n <= 1;
         end 
     end
 
     //mosi
     assign mosi = spi_din[7-cnt_bit]; 
 
     //spi_dout    
     always @(posedge clk or negedge rst_n)begin 
         if(!rst_n)begin
             spi_dout <= 0;
         end 
         else if(cs_n == 0 && cnt_sck == `SCK_PERIOD-1)begin 
             spi_dout[7-cnt_bit] <= miso;
         end 
         else begin 
            spi_dout <= spi_dout; 
         end 
     end
  
 
 //spi_one_byte_done
     assign  spi_one_byte_done = end_cnt_bit;
 
 //busy
     always @(posedge clk or negedge rst_n)begin 
         if(!rst_n)begin
             busy <= 0;
         end 
         else if(add_cnt_bit && end_cnt_bit == 0)begin 
             busy <= 1;
         end 
         else if(end_cnt_bit)begin 
             busy <= 0;
         end 
     end
 
                       
 endmodule
spi_master

 

 //参数定义
 //spi时钟参数
 `define  SCK_PERIOD  20
 `define  SCK_HALF    10
 

 //spi command
 `define  CMD_WREN       8'h06               // 
 `define  CMD_WRDI       8'h04               // 
 `define  CMD_RDID       8'h9f               // 
 `define  CMD_RDSR       8'h05               // 
 `define  CMD_WRSR       8'h01               // 
 `define  CMD_READ       8'h03               // 
 `define  CMD_FAST_READ  8'h0b               // 
 `define  CMD_PP         8'h02               // 
 `define  CMD_SE         8'hd8               // 
 `define  CMD_BE         8'hc7               // 
 `define  CMD_DP         8'hb9               // 
 `define  CMD_RES        8'hab               //  
 
 //spi byte
 `define  CMD_BYTE        1               // 命令1字节数 
 `define  ADDR_BYTE       3               // 地址3字节数 
 `define  ID_BYTE         3               // 读取id字节数 
 `define  DATA_BYTE       256             // 读取数据字节数 
 `define  RDSR_BYTE       1               // 读状态寄存器字节数
 // `define  DELAY_5MS       250_000         // 5ms 数据写入后需要等待5ms才能读出
 `define  DELAY_5MS      50         // 5ms 数据写入后需要等待5ms才能读出 
 //波特率
 `define  BAUD_9600   5208
 `define  BAUD_19200  2604
 `define  BAUD_38400  1302
 `define  BAUD_115200 434
 
 `define STOP_BIT  1'b1      //数据停止位
 `define START_BIT 1'b0      //数据开始位

 `define MF_ID  8'h20      
 `define ID1    8'hBA      
 `define ID2    8'h18 
 
 
param.v

这里的读第二个ID,手册写的BB,我实际测的一直是BA,所以就用了BA

仿真相关:

module top( 
    
    input wire       sys_clk_p,    //system clock positive
    input wire       sys_clk_n,    //system clock negative 
    input wire       rst_n ,       //reset ,low active    

    input           erase_start,
    input           write_start,
    
    // output erase_completed,  //仿真
    // output four_pp_completed,//仿真
    
    input               miso    ,
    // output              sck     ,
    output              mosi    ,
    output              cs_n        
    
);

    wire    erase_completed;//调试
    wire  four_pp_completed;//调试

wire sck;

IBUFGDS sys_clk_ibufgds 
(
.O      (sys_clk     ),//200MHz
.I      (sys_clk_p   ),
.IB     (sys_clk_n   )
);

   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_spi (
      .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(1'b0),                       // 1-bit input: User start-up clock input
      .GSR(1'b0),                       // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name)
      .GTS(1'b0),                       // 1-bit input: Global 3-state input (GTS cannot be used for the port name)
      .KEYCLEARB(1'b1),                 // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM)
      .PACK(1'b1),                      // 1-bit input: PROGRAM acknowledge input
      .USRCCLKO(sck),                   // 1-bit input: User CCLK input
      .USRCCLKTS(1'b0),                 // 1-bit input: User CCLK 3-state enable input
      .USRDONEO(1'b1),                  // 1-bit input: User DONE pin output control
      .USRDONETS(1'b1)                  // 1-bit input: User DONE 3-state enable output
   );
   
reg [5:0]     cnt_clk=0;
wire      add_cnt_clk;
wire      end_cnt_clk;
always@(posedge sys_clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt_clk<=0;
    end
    else if(add_cnt_clk)begin
        if(end_cnt_clk)
            cnt_clk<=0;
        else
            cnt_clk<=cnt_clk+1;
    end 
end 
assign add_cnt_clk = 1;
assign end_cnt_clk = add_cnt_clk && cnt_clk == 2-1;

// reg clk;//100MHz
// always@(posedge sys_clk)begin
    // if(cnt_clk<1)begin
        // clk<=1'b1;
    // end
    // else begin
        // clk<=1'b0;
    // end 
// end   

wire clk;
assign clk=sys_clk;

// wire clk_out1;
// wire clk_out2;
  // clk_wiz_0 clk_wiz_0_uut
   // (

    // .clk_out1(clk_out1),     // output clk_out1
    // .clk_out2(clk_out2),     // output clk_out2
    // .reset(~rst_n), // input reset
    // .locked(locked),       // output locked
    // .clk_in1(sys_clk));      // input clk_in1

// wire clk;
// assign clk=clk_out2 & locked;


//===========================vio=========================================
wire earse_start_vio;
wire write_start_vio;
wire read_start_vio;
vio_0 uut_vio_0 (
  .clk(clk),                    
  .probe_out0(earse_start_vio), 
  .probe_out1(write_start_vio),
  .probe_out2(read_start_vio)  
); 

reg earse_start_r1;
always@(posedge clk)begin
earse_start_r1<=earse_start_vio;
end

wire   earse_edge;
assign earse_edge= earse_start_r1 & ~earse_start_vio;

reg write_start_r1;
always@(posedge clk)begin
write_start_r1<=write_start_vio;
end

wire   write_edge;
assign write_edge= write_start_r1 & ~write_start_vio;

reg read_start_r1;
always@(posedge clk)begin
read_start_r1<=read_start_vio;
end

wire   read_edge;
assign read_edge= read_start_r1 & ~read_start_vio;    
//===========================test=========================================                             
reg         I_fl_data_en ;        
wire        end_cnt0;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        I_fl_data_en <= 0;
    end
        // else if(write_start) begin //仿真
        else if(write_edge)begin     //实际
        I_fl_data_en <= 1;
    end 
    else if(end_cnt0) begin
        I_fl_data_en <= 0;
    end     
end 

reg [15:0] cnt0;
wire      add_cnt0;
// wire      end_cnt0;
always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt0<='d0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0<=0;
        else
            cnt0<=cnt0+1;
    end 
end 
assign add_cnt0 = I_fl_data_en;
assign end_cnt0 = add_cnt0 && cnt0 == 1024-1;//256-1;

//

 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   wr_rd_state_spi_out ;
 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   dout                ;//读取数据
 (*mark_debug="true", dont_touch="true"*)wire                dout_vld            ;//数据有效
 (*mark_debug="true", dont_touch="true"*)wire                spi_vld             ;
 (*mark_debug="true", dont_touch="true"*)wire                en                  ;
 (*mark_debug="true", dont_touch="true"*)wire        [7:0]   spi_dout            ;//读取数据进入fifo                   
 (*mark_debug="true", dont_touch="true"*)wire                spi_one_byte_done   ; 
 

 // (*mark_debug="true", dont_touch="true"*) wire [1:0] key_out;
 // key_filter_fsm # (.KEY_W(2),.TIME_20MS(1_000_000))u_key_filter_fsm
 // (
     // /*input                         */.clk        (clk            ),
     // /*input                         */.rst_n    (rst_n          ),
     // /*input         [KEY_W - 1:0]    */.key_in    ({write_start,erase_start}         ),
     // /*output         [KEY_W - 1:0]    */.key_out    (key_out        )
 // );

 wr_control u_wr_control( 
     /*input                  */.clk          (clk              ),
     /*input                  */.rst_n         (rst_n            ),
     
     // `ifdef simulation
         // `elsif
                            // .erase_start  (erase_start),// erase_start 仿真
                            // .write_start  (write_start),//write_start

                            .erase_start  (earse_edge  ),//erase_start 调试
                            .write_start  (write_edge  ),//write_start
                            
                            .erase_completed(erase_completed),
                            .four_pp_completed(four_pp_completed),
                            
                            .read_start(read_edge),
    // `endif    
    
     /*input               */.spi_one_byte_done (spi_one_byte_done          ),
     /*input        [7:0]  */.din          (cnt0[7:0]),//(rx_byte          ),//
     /*input               */.din_vld      (I_fl_data_en),//(rx_byte_vld      ),//
     
     /*input        [7:0]  */.read_data       (spi_dout         ),
     
     /*output    reg [7:0] */.wr_rd_state_spi_out  (wr_rd_state_spi_out           ),
     /*output      [7:0]   */.read_flash_data_fifo_out  (dout             ),
     /*output              */.read_flash_data_fifo_out_vld  (dout_vld         ),
     /*output              */.en           (en               ),
     /*output              */.spi_vld      (spi_vld          ) 
 
 );
 
  
 spi_master u_spi_master( 
     /*input               */.clk        (clk            ),
     /*input               */.rst_n      (rst_n          ),
     /*input               */.en         (en             ),
     /*input       [1:0]   */.spi_mode   (2'b00          ),
     /*input               */.spi_vld    (spi_vld        ),
     /*input        [7:0]  */.spi_din    (wr_rd_state_spi_out),//写入数据
     /*input               */.miso       (miso           ),
     /*output    reg       */.sck        (sck            ),
     /*output  reg         */.cs_n       (cs_n           ),
     /*output              */.mosi       (mosi           ),
     /*output  reg [7:0]   */.spi_dout   (spi_dout       ),//读取数据
     /*output  reg         */.busy       (         ),
     /*output              */.spi_one_byte_done  (spi_one_byte_done        ) 
 );                                 
 



endmodule 
top

 

`timescale 1ns / 1ps
// `define        simulation
module top_tb();

//时钟与复位
reg sys_clk_p;
reg rst_n;

//uut的输入信号
reg erase_start ;
reg write_start ;
reg miso;
//时钟周期,单位为ns,
parameter CYCLE = 5;
//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;

//待测试的模块例化

top uut(
.sys_clk_p(sys_clk_p),
.sys_clk_n(~sys_clk_p),
.rst_n (rst_n),

.erase_start  (erase_start ),
.write_start  (write_start ),

.miso (miso),
.mosi (mosi),
.cs_n (cs_n)
);

//生成本地时钟200M
initial begin
    sys_clk_p =0;
    forever
    #(CYCLE/2)
    sys_clk_p =~sys_clk_p;    
end 
//产生复位信号
initial begin
    rst_n =1;
    #2;
    rst_n =0;    
    #(CYCLE*RST_TIME)
    rst_n =1;
end 

initial begin
write_start=0;

  
    erase_start =0;
    #1000
    erase_start =1; 
    #100
    erase_start =0; 


#35_0000  //擦除4块
write_start=1;    
#100
write_start=0;

#180_0000
write_start=1;    
#100
write_start=0; 
 
// #10_0000  //擦除4块
// write_start=1;    
// #100
// write_start=0;

// #180_0000
// write_start=1;    
// #100
// write_start=0;



end 


initial begin
    miso =0;
      
end 

endmodule 
top_tb

 

注意:里面代码延时部分没有使用,是通过读状态寄存器来判断忙不忙,来决定进行下一次操作;文末有参考原文连接将的更加详细;

 

xilinx在线升级相关:

这里icap控制跳转的代码,测试可用,以下是原文连接:

https://blog.csdn.net/qq_43557686/article/details/134750377?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522028715b643b80c588730a73e4c1310ae%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=028715b643b80c588730a73e4c1310ae&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-134750377-null-null.nonecase&utm_term=icap&spm=1018.2226.3001.4450

/* 
 * file            : multiboot_ctrl.v
 * author        : 今朝无言
 * Lab            : WHU-EIS-LMSWE
 * date            : 2023-11-30
 * version        : v1.0
 * description    : ICAP 原语实现程控 multiboot(多重启动),K7需要使用 ICAPE2 原语
 * Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */

module multiboot_ctrl(
input    wire            clk,
input    wire            rst_n,

input    wire            multiboot_start,    //触发Multiboot, 上升沿有效
input    wire    [31:0]    multiboot_addr,        //要启动的Muliboot Image的起始地址

output    reg                busy
);

//-------------------ICAPE2原语-----------------------------
wire            ICAPE2_CLK;
wire    [31:0]    ICAPE2_O;
reg                ICAPE2_CSIB;
wire    [31:0]    ICAPE2_I;
reg                ICAPE2_RDWRB;

assign    ICAPE2_CLK    = clk;

ICAPE2 #(
     .DEVICE_ID            (32'h3631093),    // Specifies the pre-programmed Device ID value to be used for simulation purposes. K7-100T的为32'h3631093
     .ICAP_WIDTH        ("X32"),        // Specifies the input and output data width.
     .SIM_CFG_FILE_NAME    ("NONE")        // Specifies the Raw Bitstream (RBT) file to be parsed by the simulation model.
)
ICAPE2_inst(
     .O                    (ICAPE2_O),        // 32-bit output: Configuration data output bus
     .CLK                (ICAPE2_CLK),    // 1-bit input: Clock Input
     .CSIB                (ICAPE2_CSIB),    // 1-bit input: Active-Low ICAP Enable
     .I                    (ICAPE2_I),        // 32-bit input: Configuration data input bus
     .RDWRB                (ICAPE2_RDWRB)    // 1-bit input: Read/Write Select input    1对应rd,0对应wr
);

wire    [31:0]    Dummy        = 32'hFFFFFFFF;
wire    [31:0]    Sync_Word    = 32'hAA995566;
wire    [31:0]    NOOP        = 32'h20000000;
wire    [31:0]    WR_WBSTAR    = 32'h30020001;

/*When using ICAPE2 to set the WBSTAR address, the 24 most significant address bits should be written
  to WBSTAR[23:0]. For SPI 32-bit addressing mode, WBSTAR[23:0] are sent as address bits [31:8]. The
  lower 8 bits of the address are undefined and the value could be as high as 0xFF. Any bitstream
  at the WBSTAR address should contain 256 dummy bytes before the start of the bitstream.*/
wire    [31:0]    WBSTAR        = {3'b000, 5'h0, multiboot_addr[31:8]};

wire    [31:0]    WR_CMD        = 32'h30008001;
wire    [31:0]    IPROG        = 32'h0000000F;

//ICAPE2位翻转
reg        [31:0]    wrdat;
assign    ICAPE2_I    = {wrdat[24], wrdat[25], wrdat[26], wrdat[27], wrdat[28], wrdat[29], wrdat[30], wrdat[31], 
                       wrdat[16], wrdat[17], wrdat[18], wrdat[19], wrdat[20], wrdat[21], wrdat[22], wrdat[23], 
                       wrdat[8], wrdat[9], wrdat[10], wrdat[11], wrdat[12], wrdat[13], wrdat[14], wrdat[15], 
                       wrdat[0], wrdat[1], wrdat[2], wrdat[3], wrdat[4], wrdat[5], wrdat[6], wrdat[7]};

//------------------------FSM----------------------------------
localparam    S_IDLE            = 16'h0001;
localparam    S_DUMMY            = 16'h0002;
localparam    S_SYN_WORD        = 16'h0004;
localparam    S_NOOP1            = 16'h0008;
localparam    S_WR_WBSTAR        = 16'h0010;
localparam    S_WBSTAR        = 16'h0020;
localparam    S_WR_CMD        = 16'h0040;
localparam    S_IPROG            = 16'h0080;
localparam    S_NOOP2            = 16'h0100;
localparam    S_STOP            = 16'h0200;

(*mark_debug="true", dont_touch="true"*)wire    multiboot_start_pe;
reg        multiboot_start_d0;
reg        multiboot_start_d1;

assign    multiboot_start_pe    = multiboot_start_d0 & (~multiboot_start_d1);

always @(posedge clk) begin
    multiboot_start_d0        <= multiboot_start;
    multiboot_start_d1        <= multiboot_start_d0;
end

(*mark_debug="true", dont_touch="true"*) reg        [15:0]    state        = S_IDLE;
reg        [15:0]    next_state;

always @(posedge clk) begin
    if(~rst_n) begin
        state    <= S_IDLE;
    end
    else begin
        state    <= next_state;
    end
end

always @(*) begin
    case(state)
    S_IDLE: begin
        if(multiboot_start_pe) begin
            next_state    <= S_DUMMY;
        end
        else begin
            next_state    <= S_IDLE;
        end
    end
    S_DUMMY:        next_state    <= S_SYN_WORD;
    S_SYN_WORD:        next_state    <= S_NOOP1;
    S_NOOP1:        next_state    <= S_WR_WBSTAR;
    S_WR_WBSTAR:    next_state    <= S_WBSTAR;
    S_WBSTAR:        next_state    <= S_WR_CMD;
    S_WR_CMD:        next_state    <= S_IPROG;
    S_IPROG:        next_state    <= S_NOOP2;
    S_NOOP2:        next_state    <= S_STOP;
    S_STOP:            next_state    <= S_IDLE;
    default:        next_state    <= S_IDLE;
    endcase
end

always @(posedge clk) begin
    case(state)
    S_IDLE: begin
        wrdat            <= 32'd0;
        ICAPE2_CSIB        <= 1'b1;
        ICAPE2_RDWRB    <= 1'b1;
    end
    S_DUMMY: begin
        wrdat            <= Dummy;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_SYN_WORD: begin
        wrdat            <= Sync_Word;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_NOOP1: begin
        wrdat            <= NOOP;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_WR_WBSTAR: begin
        wrdat            <= WR_WBSTAR;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_WBSTAR: begin
        wrdat            <= WBSTAR;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_WR_CMD: begin
        wrdat            <= WR_CMD;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_IPROG: begin
        wrdat            <= IPROG;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_NOOP2: begin
        wrdat            <= NOOP;
        ICAPE2_CSIB        <= 1'b0;
        ICAPE2_RDWRB    <= 1'b0;
    end
    S_STOP: begin
        wrdat            <= 32'd0;
        ICAPE2_CSIB        <= 1'b1;
        ICAPE2_RDWRB    <= 1'b1;
    end
    default: begin
        wrdat            <= 32'd0;
        ICAPE2_CSIB        <= 1'b1;
        ICAPE2_RDWRB    <= 1'b1;
    end
    endcase
end

always@(*)begin
    if(state==S_IDLE)
        busy    <= 1'b0;
    else 
        busy    <= 1'b1;
end

endmodule
multiboot_ctrl

 

golden中icap

//上电初始化信号
// parameter    STA_EN_DLY    =    32'd399_999_999;
parameter    STA_EN_DLY    =    32'd7_999_999_999;//64s
(*mark_debug="true", dont_touch="true"*)reg [32:0] power_ini_cnt;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        power_ini_cnt<='d0;
    end
    else if(power_ini_cnt < STA_EN_DLY)begin
        power_ini_cnt<=power_ini_cnt+1'b1;
    end
    else begin
        power_ini_cnt<=power_ini_cnt;
    end    
end 

reg power_en;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        power_en<= 1'b0;
    end else if(power_ini_cnt == STA_EN_DLY) begin
        power_en<= 1'b1;
    end else begin
        power_en<= 1'd0;
    end
end

reg power_en_r1;
reg power_en_r2;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        power_en_r1 <= 1'b0;
        power_en_r2 <= 1'b0;
    end else begin
        power_en_r1 <= power_en;
        power_en_r2 <= power_en_r1;
    end
end

//
(*mark_debug="true", dont_touch="true"*)wire   power_ini_pos;
assign power_ini_pos = power_en_r1 & ~power_en_r2;
//
(*mark_debug="true", dont_touch="true"*)reg erase_write_start_reg;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        erase_write_start_reg<= 1'b0;
    end else if(erase_start|write_start) begin
        erase_write_start_reg<= 1'b1;
    end else begin
        erase_write_start_reg<= erase_write_start_reg;
    end
end
//
(*mark_debug="true", dont_touch="true"*)reg multiboot_start;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        multiboot_start<= 1'b0;
    end else if(power_ini_pos&(~erase_write_start_reg)) begin
        multiboot_start<= 1'b1;
    end else if(update_end)begin
        multiboot_start<= 1'b0;//1'b0不跳//1'b1跳
    end else begin
        multiboot_start<= 1'b0;
    end
end

//
multiboot_ctrl multiboot_ctrl_inst(
    .clk                (clk),//(sys_clk),
    .rst_n                (rst_n),

    .multiboot_start    (multiboot_start),//(~key_in[0]),//key_in[0]---key1 key_in[1]---key2 
    .multiboot_addr        (32'h80000000),    //实际是(32'h00800000),模块内部移动了8位        //加载0x01000000处的Multiboot Image

    .busy                ()
);
icap_Jump

 update中icap

//CMD_START_UPDATE  接收开始升级指令/用于跳转到golden区
reg start_update;
always @(posedge clk or negedge rst_n)
begin
    if (~rst_n)
      start_update<= 1'b0 ;
    else if(~header[0] && header_argu == CMD_START_UPDATE && cmd_mac_addr == local_mac_addr)
    begin
      if (cmd_cnt == 'd12)//
        start_update<=udp_rec_ram_rdata[0] ;
      else 
        start_update<=1'b0 ;
    end
end
//
multiboot_ctrl multiboot_ctrl_inst(
    .clk                (clk),//(sys_clk),
    .rst_n                (rst_n),

    .multiboot_start    (start_update),//(~key_in[0]),//key_in[0]---key1 key_in[1]---key2 
    .multiboot_addr        (32'h00000000),//加载0x01000000处的Multiboot Image

    .busy                ()
);
update_icap

 

multiboot_addr的地址这里写的是0x8000_0000,实际上在multiboot_ctrl内部做了8位偏移,而我使用的FLASH型号的S25FL128,采用的是24位地址。实际上跳转地址是0x0080_0000,经过仿真输出的是0x0080_0000;

XDC相关:

############## NET - IOSTANDARD ######################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
#############SPI Configurate Setting##################
#set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]


set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.TIMER_CFG 32'h0007A120 [current_design]

##for golden image
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
#set_property BITSTREAM.CONFIG.NEXT_CONFIG_ADDR 32'h00800000 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
##for golden image

 

############## NET - IOSTANDARD ######################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
#############SPI Configurate Setting##################
#set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]


set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.TIMER_CFG 32'h0007A120 [current_design]

#for multiboot image
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
#for multiboot image

 

 

使用icap跳转升级流程:

1.升级指令顺序:

第一步,首先发送第六条升级启动指令,该指令将update区跳转到golden区,(如果已再golden区则可不用)

第二步,获取golden版本号,如果获取到golden版本号说明跳转成功

第三步,golden区版本号获取成功,开始进行擦除操作,

第四步,收到擦除完成信号,可以发送写升级包数据命令;发一包(1024字节写4页)后,收到应答后再下发一包,直到所有包发完,

第五步,掉电,拔掉jtag下载线,重启

 

2.注意事项:

每次重启后,先进入golden版本,一分钟内如果未收到擦除写flash命令,一分钟后,自动跳转到update版本;如果update版本破坏,则回退至golden版本,

如果需要升级,则需要跳转到golden区,update版本不具有擦写flash权,防止升级掉电版本不回退;

 

这里有一个问题:当update区没有数据或者数据破坏时,可以退回golden区,如果没有升级操作,两个区会一直来回跳,如果此时想使用golden区,那么这里的方法是,当进入golden区,一分钟内,发送擦除指令,这样就会一直停留在golden区,

 

使用工具:

软件:vivado2019.1

使用的FPGA型号为XC7A100T-2FGG4841,属于Xilinx 公司 Artix-7 系列的产品,速度等级为 2,温度等级为工业级。此型号为 FGG484封装,484 个引脚。

使用了一片 容量16M的 QSPI FLASH 芯片,型号为 N25Q128,它使用 3.3V CMOS 电压标准。由于它的非易失特性,在使用中,QSPIFLASH 可以作为 FPGA 系统的启动镜像。

vivado可选Mt25ql128-spi-x1_x2_x4

 

844b80173e9299dd573cb1a9751a13d7

image

 

image

 

 

 

 

 

 

 

c49db7e2c579968e15e445203c2de37d

 

合成mcs,

image

 

9f2653aa6586124551bc30a9cc55caf5

 

 

参考连接:

flash参考代码连接:

【FPGA】SPI协议详解及对flash读写操作(玩嵌入式谁会脱发呀)

https://blog.csdn.net/li_lys/article/details/123146497

 

K7系列FPGA多重启动(Multiboot)(今朝无言

https://blog.csdn.net/qq_43557686/article/details/134750377?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522028715b643b80c588730a73e4c1310ae%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=028715b643b80c588730a73e4c1310ae&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-134750377-null-null.nonecase&utm_term=icap&spm=1018.2226.3001.4450

 

 

FPGA在线升级 -- Multiboot(热爱学习地派大星

https://blog.csdn.net/weixin_51418325/article/details/144370219?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-5-144370219-blog-136070049.235^v43^pc_blog_bottom_relevance_base8&spm=1001.2101.3001.4242.4&utm_relevant_index=7

 

 

感谢各位前辈的指路,记录一下,

 

posted on 2025-11-21 15:04  taylorrrrrrrrrr  阅读(2)  评论(0)    收藏  举报