FPGA零基础学习:SPI 协议驱动设计(下)

2021-03-23 09:54:37 浏览数 (1)

由于单篇字数限制,本篇分为上下,下篇接续上篇。

pp设计实现

该模块负责将外部写fifo中的数据写入到flash中。wr_fifo_rd为写fifo的读使能信号,wrdata为从写fifo中读出的数据,wr_len为需要写入flash中数据的长度,wr_addr为写入地址。

该模块采用状态机实现。PP_STATE(发送pp命令),H_ADDR(发送高八位地址)、M_ADDR(发送中间八位地址),L_ADDR(发送低八位地址)、RDFIFO(读写fifo)、FIFO_WAIT(等待读写fifo的数据输出)、SEND(发送8bit数据)、SEND_WAIT(发送等待,发送完成后判断是否发送完成)。对于所有的脉冲信号,没有赋值的位置,全部赋值为0。

cnt为记录已经发送的数据个数。

添加描述

设计代码为:

代码语言:javascript复制
module pp (

  input   wire                  clk,
  input   wire                  rst_n,
  
  input   wire                  pp_en,
  output  reg                   pp_done,
  output  reg                   wr_fifo_rd,
  input   wire    [7:0]         wrdata,
  input   wire    [8:0]         wr_len,
  input   wire    [23:0]        wr_addr,
  
  output  reg                   pp_send_en,
  output  reg     [7:0]         pp_send_data,
  input   wire                  spi_send_done
);

  localparam      PP_STATE    = 8'b0000_0001;
  localparam      H_ADDR      = 8'b0000_0010;
  localparam      M_ADDR      = 8'b0000_0100;
  localparam      L_ADDR      = 8'b0000_1000;
  localparam      RDFIFO      = 8'b0001_0000;
  localparam      FIFO_WAIT   = 8'b0010_0000;
  localparam      SEND        = 8'b0100_0000;
  localparam      SEND_WAIT   = 8'b1000_0000;
  
  reg             [7:0]         c_state;
  reg             [7:0]         n_state;
  reg             [8:0]         cnt;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= PP_STATE;
    else
      c_state <= n_state;
  end
  
  always @ * begin
    case (c_state)
      PP_STATE        :   begin
        if (pp_en == 1'b0)
          n_state = PP_STATE;
        else
          n_state = H_ADDR;
      end
      
      H_ADDR          :   begin
        if (spi_send_done == 1'b1)
          n_state = M_ADDR;
        else
          n_state = H_ADDR;
      end
      
      M_ADDR          :   begin
        if (spi_send_done == 1'b1)
          n_state = L_ADDR;
        else
          n_state = M_ADDR;
      end
      
      L_ADDR          :   begin
        if (spi_send_done == 1'b1)
          n_state = RDFIFO;
        else
          n_state = L_ADDR;
      end
      
      RDFIFO          :   begin
        if (spi_send_done == 1'b1)
          n_state = FIFO_WAIT;
        else
          n_state = RDFIFO;
      end
      
      FIFO_WAIT       :   begin
        n_state = SEND;
      end
      
      SEND            :   begin
        n_state = SEND_WAIT;
      end
      
      SEND_WAIT       :   begin
        if (spi_send_done == 1'b1)
          if (cnt == wr_len)
            n_state = PP_STATE;
          else
            n_state = FIFO_WAIT;
        else
          n_state = SEND_WAIT;
      end
      
      default       :   n_state = PP_STATE;
      
    endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      pp_send_en <= 1'b0;
    else
      case (c_state)
        PP_STATE      :   begin
          if  (pp_en == 1'b1)
            pp_send_en <= 1'b1;
          else
            pp_send_en <= 1'b0;
        end
      
        H_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_en <= 1'b1;
          else
            pp_send_en <= 1'b0;
        end
        
        M_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_en <= 1'b1;
          else
            pp_send_en <= 1'b0;
        end
        
        L_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_en <= 1'b1;
          else
            pp_send_en <= 1'b0;
        end
        
        SEND          : begin
          pp_send_en <= 1'b1;
        end
        
        default       :   pp_send_en <= 1'b0;
      endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      pp_send_data <= 8'd0;
    else
      case (c_state)
        PP_STATE      :   begin
          if  (pp_en == 1'b1)
            pp_send_data <= 8'h02;
          else
            pp_send_data <= 8'd0;
        end
      
        H_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_data <= wr_addr[23:16];
          else
            pp_send_data <= 8'd0;
        end
        
        M_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_data <= wr_addr[15:8];
          else
            pp_send_data <= 8'd0;
        end
        
        L_ADDR        :   begin
          if (spi_send_done == 1'b1)
            pp_send_data <= wr_addr[7:0];
          else
            pp_send_data <= 8'd0;
        end
        
        SEND          : begin
          pp_send_data <= wrdata;
        end
        
        default       :   pp_send_data <= 8'd0;
      endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      pp_done <= 1'b0;
    else
      if (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt == wr_len)
        pp_done <= 1'b1;
      else
        pp_done <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      cnt <= 9'd0;
    else
      if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))
        cnt <= cnt   1'b1;
      else
        if (c_state == PP_STATE)
          cnt <= 9'd0;
        else
          cnt <= cnt;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wr_fifo_rd <= 1'b1;
    else
      if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))
        wr_fifo_rd <= 1'b1;
      else
        wr_fifo_rd <= 1'b0;
  end
  
endmodule

rdsr设计实现

本模块的功能为读取m25p16的状态寄存器,主要检测状态寄存器的最低位(WIP)。

WIP(write in progress :正在进行写进程),该bie位表示了flash内部是否在进行写进程。如果处于写进程时,flash忽略外部所有的命令,所以建议在执行任何命令前,首先进行检测该位。1表示正在写进程中,0表示不处于写进程。

如果检测到正在写进程中,进行延迟1ms,然后再次读取该位状态。直到写进程结束。

本模块采用状态机设计实现。ILDE(发送读状态寄存器命令)、RDSRSTATE(发送读使能)、WIP(判断wip位)、 DELAY1ms(延迟1ms)。cnt为延迟1ms的计数器。

添加描述

设计代码为:

代码语言:javascript复制
module rdsr (

  input   wire                    clk,
  input   wire                    rst_n,
  
  input   wire                    rdsr_en,
  output  reg                     rdsr_done,
  
  output  reg                     rdsr_send_en,
  output  reg       [7:0]         rdsr_send_data,
  input   wire                    spi_send_done,
  
  output  reg                     rdsr_read_en,
  input   wire      [7:0]         spi_read_data,
  input   wire                    spi_read_done
);

  parameter     T_1ms           =       50_000;
  
  
  localparam    IDLE            =       4'b0001;
  localparam    RDSRSTATE       =       4'b0010;
  localparam    WIP             =       4'b0100;
  localparam    DELAY1ms        =       4'b1000;
  
  reg               [3:0]         c_state;
  reg               [3:0]         n_state;
  reg               [15:0]        cnt;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= IDLE;
    else
      c_state <= n_state;
  end

  always @ * begin
    case (c_state)
      IDLE          :   begin
        if (rdsr_en == 1'b0)
          n_state = IDLE;
        else
          n_state = RDSRSTATE;
      end
      
      RDSRSTATE     :   begin
        if (spi_send_done == 1'b1)
          n_state = WIP;
        else
          n_state = RDSRSTATE;
      end
      
      WIP           :   begin
        if (spi_read_done == 1'b0)
          n_state = WIP;
        else
          if (spi_read_data[0] == 1'b0)
            n_state = IDLE;
          else
            n_state = DELAY1ms;
      end
      
      DELAY1ms      :   begin
        if (cnt < T_1ms - 1'b1)
          n_state = DELAY1ms;
        else
          n_state = RDSRSTATE;
      end
      
      default       :   n_state = IDLE;
    
    endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      cnt <= 16'd0;
    else
      if (c_state == DELAY1ms && cnt < T_1ms - 1'b1)
        cnt <= cnt   1'b1;
      else
        cnt <= 16'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdsr_done <= 1'b0;
    else
      if (c_state == WIP && spi_read_done == 1'b1 && spi_read_data[0] == 1'b0)
        rdsr_done <= 1'b1;
      else
        rdsr_done <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdsr_send_data <= 8'd0;
    else
      if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))
        rdsr_send_data <= 8'h05;
      else
        rdsr_send_data <= 8'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdsr_send_en <= 1'b0;
    else
      if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))
        rdsr_send_en <= 1'b1;
      else
        rdsr_send_en <= 1'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdsr_read_en <= 1'b0;
    else
      if (c_state == RDSRSTATE && spi_send_done == 1'b1)
        rdsr_read_en <= 1'b1;
      else
        rdsr_read_en <= 1'b0;
  end
  
endmodule

rdid设计实现

该模块负责读取flash的ID(2015),验证ID的正确性。

该模块采用状态机的方式实现。IDLE(等待读取ID的命令)、IDSTATE1(读取高八位ID)、IDSTATE2(读取中间八位ID)、IDSTATE3(读取低八位ID)、ID_CHECK(检测ID的正确性)。

状态转移图如下:

添加描述

设计代码为:

代码语言:javascript复制
module rdid (

  input     wire                  clk,
  input     wire                  rst_n,
  
  input     wire                  rdid_en,
  output    reg                   rdid_done,
  
  output    reg                   rdid_send_en,
  output    reg     [7:0]         rdid_send_data,
  input     wire                  spi_send_done,
  
  output    reg                   rdid_read_en,
  input     wire                  spi_read_done,
  input     wire    [7:0]         spi_read_data
);

  localparam        IDLE          = 5'b00001;
  localparam        IDSTATE1      = 5'b00010;
  localparam        IDSTATE2      = 5'b00100;
  localparam        IDSTATE3      = 5'b01000;
  localparam        ID_CHECK      = 5'b10000;
  
  reg               [4:0]         c_state;
  reg               [4:0]         n_state;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= IDLE;
    else
      c_state <= n_state;
  end
  
  always @ * begin
    case (c_state)
      IDLE          :     begin
        if (rdid_en == 1'b1)
          n_state = IDSTATE1;
        else
          n_state = IDLE;
      end
      
      IDSTATE1      :     begin
        if (spi_send_done == 1'b1)
          n_state = IDSTATE2;
        else
          n_state = IDSTATE1;
      end
      
      IDSTATE2      :     begin
        if (spi_read_done == 1'b1 && spi_read_data == 8'h20)
          n_state = IDSTATE3;
        else
          n_state = IDSTATE2;
      end
      
      IDSTATE3      :     begin
        if (spi_read_done == 1'b1 && spi_read_data == 8'h20)
          n_state = ID_CHECK;
        else
          n_state = IDSTATE3;
      end
      
      ID_CHECK      :     begin
        if (spi_read_done == 1'b1 && spi_read_data == 8'h15)
          n_state = IDLE;
        else
          n_state = ID_CHECK;
      end
      
      default       :   n_state = IDLE;
    endcase
  end

  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdid_send_data <= 8'd0;
    else
      if (c_state == IDLE && rdid_en == 1'b1)
        rdid_send_data <= 8'h9f;
      else
        rdid_send_data <= 8'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdid_send_en <= 1'b0;
    else
      if (c_state == IDLE && rdid_en == 1'b1)
        rdid_send_en <= 1'b1;
      else
        rdid_send_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdid_read_en <= 1'b0;
    else
      if ((c_state == IDSTATE1 && spi_send_done == 1'b1) || (c_state == IDSTATE2 && spi_read_done == 1'b1 && spi_read_data == 8'h20) || (c_state == IDSTATE3 && spi_read_done == 1'b1 && spi_read_data == 8'h20))
        rdid_read_en <= 1'b1;
      else
        rdid_read_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdid_done <= 1'b0;
    else
      if (c_state == ID_CHECK && spi_read_done == 1'b1 && spi_read_data == 8'h15)
        rdid_done <= 1'b1;
      else
        rdid_done <= 1'b0;
  end
  
endmodule

read_ctrl设计实现

该模块负责将flash的数据读出,写入到输出缓存中。

该模块采用状态机实现。RD_STATE(等待读命令)、H_ADDR(发送高八位地址)、M_ADDR(发送中间八位地址)、L_ADDR(发送低八位地址)、RDDATA(读取数据)、WRFIFO(将读出的数据写入到FIFO中)、CHECK_LEN(判断读取的长度)。

状态转移图如下:

添加描述

设计代码为:

代码语言:javascript复制
module read_ctrl (

  input     wire                clk,
  input     wire                rst_n,
  
  input     wire                read_en,
  input     wire    [23:0]      rd_addr,
  input     wire    [8:0]       rd_len,
  
  output    reg     [7:0]       rddata,
  output    reg                 rd_fifo_wr,
  
  output    reg                 read_done,
  
  output    reg                 read_send_en,
  output    reg     [7:0]       read_send_data,
  input     wire                spi_send_done,
  
  output    reg                 read_read_en,
  input     wire                spi_read_done,
  input     wire    [7:0]       spi_read_data
);

  localparam        RD_STATE    = 7'b000_0001;
  localparam        H_ADDR      = 7'b000_0010;
  localparam        M_ADDR      = 7'b000_0100;
  localparam        L_ADDR      = 7'b000_1000;
  localparam        RDDATA      = 7'b001_0000;
  localparam        WRFIFO      = 7'b010_0000;
  localparam        CHECK_LEN   = 7'b100_0000;
  
  reg               [6:0]       c_state;
  reg               [6:0]       n_state;
  reg               [8:0]       cnt;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= RD_STATE;
    else
      c_state <= n_state;
  end
  
  always @ * begin
    case (c_state)
      RD_STATE      :   begin
        if (read_en == 1'b1)
          n_state = H_ADDR;
        else
          n_state = RD_STATE;
      end
      
      H_ADDR        :   begin
        if (spi_send_done == 1'b1)
          n_state = M_ADDR;
        else
          n_state = H_ADDR;
      end
      
      M_ADDR        :   begin
        if (spi_send_done == 1'b1)
          n_state = L_ADDR;
        else
          n_state = M_ADDR;
      end
      
      L_ADDR        :   begin
        if (spi_send_done == 1'b1)
          n_state = RDDATA;
        else
          n_state = L_ADDR;
      end
      
      RDDATA        :   begin
        if (spi_send_done == 1'b1)
          n_state = WRFIFO;
        else
          n_state = RDDATA;
      end
      
      WRFIFO        :   begin
        if (spi_read_done == 1'b1)
          n_state = CHECK_LEN;
        else
          n_state = WRFIFO;
      end
      
      CHECK_LEN     :   begin
        if (cnt == rd_len)
          n_state = RD_STATE;
        else
          n_state = WRFIFO;
      end
      
      default       :   n_state = RD_STATE;
    
    endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      cnt <= 9'd0;
    else
      if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))
        cnt <= cnt   1'b1;
      else
        if (c_state == RD_STATE)
          cnt <= 9'd0;
        else  
          cnt <= cnt;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      read_read_en <= 1'b0;
    else
      if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))
        read_read_en <= 1'b1;
      else
        read_read_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      read_done <= 1'b0;
    else
      if (c_state == CHECK_LEN && cnt == rd_len)
        read_done <= 1'b1;
      else
        read_done <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rd_fifo_wr <= 1'b0;
    else
      if (c_state == WRFIFO && spi_read_done == 1'b1)
        rd_fifo_wr <= 1'b1;
      else
        rd_fifo_wr <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rddata <= 8'd0;
    else
      if (c_state == WRFIFO && spi_read_done == 1'b1)
        rddata <= spi_read_data;
      else
        rddata <= 8'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      read_send_en <= 1'b0;
    else
      case (c_state)
        RD_STATE      :   begin
          if  (read_en == 1'b1)
            read_send_en <= 1'b1;
          else
            read_send_en <= 1'b0;
        end
      
        H_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_en <= 1'b1;
          else
            read_send_en <= 1'b0;
        end
        
        M_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_en <= 1'b1;
          else
            read_send_en <= 1'b0;
        end
        
        L_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_en <= 1'b1;
          else
            read_send_en <= 1'b0;
        end
     
        default       :   read_send_en <= 1'b0;
      endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      read_send_data <= 8'd0;
    else
      case (c_state)
        RD_STATE      :   begin
          if  (read_en == 1'b1)
            read_send_data <= 8'h03;
          else
            read_send_data <= 8'd0;
        end
      
        H_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_data <= rd_addr[23:16];
          else
            read_send_data <= 8'd0;
        end
        
        M_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_data <= rd_addr[15:8];
          else
            read_send_data <= 8'd0;
        end
        
        L_ADDR        :   begin
          if (spi_send_done == 1'b1)
            read_send_data <= rd_addr[7:0];
          else
            read_send_data <= 8'd0;
        end
        
        default       :   read_send_data <= 8'd0;
      endcase
  end
  
endmodule

wr_fifo和rd_fifo调用

两个fifo的宽度设置为8,深度设置为256,同步fifo,带有复位。

ctrl设计实现

该模块根据外部的命令,按照m25p16的执行规则,进行控制各个模块的执行。

该模块采用状态机实现。INIT_RDSR(读WIP),INIT_RDID(读ID),INIT_ID(判断ID),WIP(读WIP),WIP_DONE(等待WIP),IDLE(空闲状态),**STATE(执行对应的命令),**WREN(打开flash的写使能)。在进行任何命令前,都检查wip。

状态转移图如下:

添加描述

在不同的状态,mux_sel选择对应的命令通过。

drive_busy只有在IDLE状态才是低电平。

spi_cs_n信号, DLE状态为高电平、WIP_DONE(INIT_RDID)中spi_read_done信号为高时 (保证能够多次读取状态寄存器)、在其他状态发生切换时,spi_cs_n 为高电平,否则为低电平。

设计代码为:

代码语言:javascript复制
module ctrl (

  input     wire                clk,
  input     wire                rst_n,
  
  input     wire                flag_be,
  input     wire                flag_se,
  input     wire                flag_wr,
  input     wire                flag_rd,
  input     wire    [23:0]      addr,
  input     wire    [8:0]       len,
  
  output    wire                drive_busy,
  
  output    reg                 spi_cs_n,
  
  input     wire                spi_read_done,
  output    reg                 rdsr_en,
  input     wire                rdsr_done,
  
  output    reg                 wren_en,
  input     wire                wren_done,
  
  output    reg                 pp_en,
  output    reg     [23:0]      wr_addr,
  output    reg     [8:0]       wr_len,
  input     wire                pp_done,
  
  output    reg                 be_en,
  input     wire                be_done,
  
  output    reg                 se_en,
  output    reg     [23:0]      se_addr,
  input     wire                se_done,
  
  output    reg                 rdid_en,
  input     wire                rdid_done,
  
  output    reg                 read_en,
  output    reg     [23:0]      rd_addr,
  output    reg     [8:0]       rd_len,
  input     wire                read_done,
  
  output    reg     [2:0]       mux_sel
);

  localparam        INIT_RDSR       =   13'b0000_0000_00001;
  localparam        INIT_RDID       =   13'b0000_0000_00010;
  localparam        INIT_ID         =   13'b0000_0000_00100;
  localparam        WIP             =   13'b0000_0000_01000;
  localparam        WIP_DONE        =   13'b0000_0000_10000;
  localparam        IDLE            =   13'b0000_0001_00000;
  localparam        RDSTATE         =   13'b0000_0010_00000;
  localparam        PPWREN          =   13'b0000_0100_00000;
  localparam        PPSTATE         =   13'b0000_1000_00000;
  localparam        SEWREN          =   13'b0001_0000_00000;
  localparam        SESTATE         =   13'b0010_0000_00000;
  localparam        BEWREN          =   13'b0100_0000_00000;
  localparam        BESTATE         =   13'b1000_0000_00000;
  
  reg               [12:0]      c_state;
  reg               [12:0]      n_state;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= INIT_RDSR;
    else
      c_state <= n_state;
  end
  
  always @ * begin
    case (c_state)
      INIT_RDSR         :   begin
        n_state = INIT_RDID;
      end
      
      INIT_RDID         :   begin
        if (rdsr_done == 1'b1)
          n_state = INIT_ID;
        else
          n_state = INIT_RDID;
      end
      
      INIT_ID           :   begin
        if (rdid_done == 1'b1)
          n_state = WIP;
        else
          n_state = INIT_ID;
      end
      
      WIP               :   begin
        n_state = WIP_DONE;
      end
      
      WIP_DONE          :   begin
        if (rdsr_done == 1'b1)
          n_state = IDLE;
        else
          n_state = WIP_DONE;
      end
      
      IDLE              :   begin
        if (flag_wr == 1'b1)
          n_state = PPWREN;
        else
          if (flag_rd == 1'b1)
            n_state = RDSTATE;
          else
            if (flag_be == 1'b1)
              n_state = BEWREN;
            else
              if (flag_se == 1'b1)
                n_state = SEWREN;
              else
                n_state = IDLE;
      end
      
      RDSTATE           :   begin
        if (read_done == 1'b1)
          n_state = WIP;
        else
          n_state = RDSTATE;
      end
      
      PPWREN            :   begin
        if (wren_done == 1'b1)
          n_state = PPSTATE;
        else
          n_state = PPWREN;
      end
      
      PPSTATE           :   begin
        if (pp_done == 1'b1)
          n_state = WIP;
        else
          n_state = PPSTATE;
      end
      
      SEWREN            :   begin
        if (wren_done == 1'b1)
          n_state = SESTATE;
        else
          n_state = SEWREN;
      end
      
      SESTATE           :   begin
        if (se_done == 1'b1)
          n_state = WIP;
        else
          n_state = SESTATE;
      end
      
      BEWREN            :   begin
        if (wren_done == 1'b1)
          n_state = BESTATE;
        else
          n_state = BEWREN;
      end
      
      BESTATE           :   begin
        if (be_done == 1'b1)
          n_state = WIP;
        else
          n_state = BESTATE;
      end
      
      default     :   n_state = INIT_RDSR;
    endcase
  end
  
  assign drive_busy = (c_state != IDLE || flag_be == 1'b1 || flag_rd == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1);
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      spi_cs_n <= 1'b1;
    else
      case (c_state)
        INIT_RDSR       :     spi_cs_n <= 1'b1;
        
        INIT_RDID       :     if (spi_read_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
        
        INIT_ID         :     if (rdid_done == 1'b1)  
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
        
        WIP             :     spi_cs_n <= 1'b1;
        
        WIP_DONE        :     if (spi_read_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
                                
        IDLE            :     spi_cs_n <= 1'b1;
        
        RDSTATE         :     if (read_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
        
        PPWREN          :     if (wren_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
                                
        PPSTATE         :     if (pp_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
      
        SEWREN          :     if (wren_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
                                
        SESTATE         :     if (se_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
      
        BEWREN          :     if (wren_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
                                
        BESTATE         :     if (be_done == 1'b1)
                                spi_cs_n <= 1'b1;
                              else
                                spi_cs_n <= 1'b0;
        
        default         :     spi_cs_n <= 1'b1;
      endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdsr_en <= 1'b0;
    else
      if (c_state == INIT_RDSR || c_state == WIP)
        rdsr_en <= 1'b1;
      else
        rdsr_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wren_en <= 1'b0;
    else
      if (c_state == IDLE && (flag_be == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1))
        wren_en <= 1'b1;
      else
        wren_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      pp_en <= 1'b0;
    else
      if (c_state == PPWREN && wren_done == 1'b1)
        pp_en <= 1'b1;
      else
        pp_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wr_len <= 9'd0;
    else
      if (c_state == IDLE && flag_wr == 1'b1)
        wr_len <= len;
      else
        wr_len <= wr_len;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wr_addr <= 24'd0;
    else
      if (c_state == IDLE && flag_wr == 1'b1)
        wr_addr <= addr;
      else
        wr_addr <= wr_addr;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      be_en <= 1'b0;
    else
      if (c_state == BEWREN && wren_done == 1'b1)
        be_en <= 1'b1;
      else
        be_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      se_en <= 1'b0;
    else
      if (c_state == SEWREN && wren_done == 1'b1)
        se_en <= 1'b1;
      else
        se_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      se_addr <= 24'd0;
    else
      if (c_state == IDLE && flag_se == 1'b1)
        se_addr <= addr;
      else
        se_addr <= se_addr;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdid_en <= 1'b0;
    else
      if (c_state == INIT_RDID && rdsr_done == 1'b1)
        rdid_en <= 1'b1;
      else
        rdid_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rd_len <= 9'd0;
    else
      if (c_state == IDLE && flag_rd == 1'b1)
        rd_len <= len;
      else
        rd_len <= rd_len;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rd_addr <= 24'd0;
    else
      if (c_state == IDLE && flag_rd == 1'b1)
        rd_addr <= addr;
      else
        rd_addr <= rd_addr;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      read_en <= 1'b0;
    else
      if (c_state == IDLE && flag_rd == 1'b1)
        read_en <= 1'b1;
      else
        read_en <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      mux_sel <= 3'd0;
    else
      case (c_state)
        INIT_RDSR       :   mux_sel <= 3'd0;
        INIT_RDID       :   mux_sel <= 3'd0;
        INIT_ID         :   mux_sel <= 3'd5;
        WIP             :   mux_sel <= 3'd0;
        WIP_DONE        :   mux_sel <= 3'd0;
        IDLE            :   mux_sel <= 3'd0;
        RDSTATE         :   mux_sel <= 3'd6;
        PPWREN          :   mux_sel <= 3'd2;
        PPSTATE         :   mux_sel <= 3'd1;
        SEWREN          :   mux_sel <= 3'd2;
        SESTATE         :   mux_sel <= 3'd4;
        BEWREN          :   mux_sel <= 3'd2;
        BESTATE         :   mux_sel <= 3'd3;
        default         :   mux_sel <= 3'd0;
      endcase
  end
  
endmodule

m25p16_drive设计实现

本模块负责连接所有二级模块,实现所有的功能。

代码语言:javascript复制
module m25p16_drive (

  input     wire                  clk,
  input     wire                  rst_n,
  
  input     wire                  wrfifo_wr,
  input     wire    [7:0]         wrfifo_data,
  
  input     wire                  flag_be,
  input     wire                  flag_se,
  input     wire                  flag_wr,
  input     wire                  flag_rd,
  
  input     wire    [23:0]        addr,
  input     wire    [8:0]         len,
  
  input     wire                  rdfifo_rd,
  output    wire    [7:0]         rdfifo_rdata,
  output    wire                  rdfifo_rdempty,
  
  output    wire                  drive_busy,
  
  output    wire                  spi_cs_n,
  output    wire                  spi_sclk,
  output    wire                  spi_mosi,
  input     wire                  spi_miso
);

  wire                            spi_send_en;
  wire              [7:0]         spi_send_data;
  wire                            spi_send_done;
  wire                            spi_read_en;
  wire              [7:0]         spi_read_data;
  wire                            spi_read_done;
  
  wire                            rdsr_send_en;
  wire              [7:0]         rdsr_send_data;
  wire                            rdsr_read_en;
  
  wire                            pp_send_en;
  wire              [7:0]         pp_send_data;
  
  wire                            wren_send_en;
  wire              [7:0]         wren_send_data;
  
  wire                            be_send_en;
  wire              [7:0]         be_send_data;
  
  wire                            se_send_en;
  wire              [7:0]         se_send_data;
  
  wire                            rdid_send_en;
  wire              [7:0]         rdid_send_data;
  wire                            rdid_read_en;
  
  wire                            read_send_en;
  wire              [7:0]         read_send_data;
  wire                            read_read_en;
  
  wire              [2:0]         mux_sel;
  
  wire                            be_en;
  wire                            be_done;
  wire                            wren_en;
  wire                            wren_done;
  wire                            se_en;
  wire              [23:0]        se_addr;
  wire                            se_done;
  wire                            pp_en;
  wire                            pp_done;
  wire                            wr_fifo_rd;
  wire              [7:0]         wrdata;
  wire              [8:0]         wr_len;
  wire              [23:0]        wr_addr;
  wire                            rdsr_en;
  wire                            rdsr_done;
  wire                            rdid_en;
  wire                            rdid_done;
  wire                            read_en;
  wire              [23:0]        rd_addr;
  wire              [8:0]         rd_len;
  wire              [7:0]         rddata;
  wire                            rd_fifo_wr;
  wire                            read_done;
  
  ctrl ctrl_inst(

      .clk            (clk),
      .rst_n          (rst_n),
      
      .flag_be        (flag_be),
      .flag_se        (flag_se),
      .flag_wr        (flag_wr),
      .flag_rd        (flag_rd),
      .addr           (addr),
      .len            (len),
      
      .drive_busy     (drive_busy),
      
      .spi_cs_n       (spi_cs_n),
      
      .spi_read_done  (spi_read_done),
      .rdsr_en        (rdsr_en),
      .rdsr_done      (rdsr_done),
      
      .wren_en        (wren_en),
      .wren_done      (wren_done),
      
      .pp_en          (pp_en),
      .wr_addr        (wr_addr),
      .wr_len         (wr_len),
      .pp_done        (pp_done),
      
      .be_en          (be_en),
      .be_done        (be_done),
      
      .se_en          (se_en),
      .se_addr        (se_addr),
      .se_done        (se_done),
      
      .rdid_en        (rdid_en),
      .rdid_done      (rdid_done),
      
      .read_en        (read_en),
      .rd_addr        (rd_addr),
      .rd_len         (rd_len),
      .read_done      (read_done),
      
      .mux_sel        (mux_sel)
    );
  
  rd_fifo  rd_fifo_inst (
      .aclr           ( ~rst_n ),
      .clock          ( clk ),
      .data           ( rddata ),
      .rdreq          ( rdfifo_rd ),
      .wrreq          ( rd_fifo_wr ),
      .empty          ( rdfifo_rdempty ),
      .q              ( rdfifo_rdata )
    );
  
  wr_fifo  wr_fifo_inst (
      .aclr           ( ~rst_n ),
      .clock          ( clk ),
      .data           ( wrfifo_data ),
      .rdreq          ( wr_fifo_rd ),
      .wrreq          ( wrfifo_wr ),
      .q              ( wrdata )
    );
  
  read_ctrl read_ctrl_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .read_en          (read_en),
      .rd_addr          (rd_addr),
      .rd_len           (rd_len),
      
      .rddata           (rddata),
      .rd_fifo_wr       (rd_fifo_wr),
      
      .read_done        (read_done),
      
      .read_send_en     (read_send_en),
      .read_send_data   (read_send_data),
      .spi_send_done    (spi_send_done),
      
      .read_read_en     (read_read_en),
      .spi_read_done    (spi_read_done),
      .spi_read_data    (spi_read_data)
    );

  rdid rdid_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .rdid_en          (rdid_en),
      .rdid_done        (rdid_done),
      
      .rdid_send_en     (rdid_send_en),
      .rdid_send_data   (rdid_send_data),
      .spi_send_done    (spi_send_done),
      
      .rdid_read_en     (rdid_read_en),
      .spi_read_done    (spi_read_done),
      .spi_read_data    (spi_read_data)
    );
    
  rdsr rdsr_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .rdsr_en          (rdsr_en),
      .rdsr_done        (rdsr_done),
      
      .rdsr_send_en     (rdsr_send_en),
      .rdsr_send_data   (rdsr_send_data),
      .spi_send_done    (spi_send_done),
      
      .rdsr_read_en     (rdsr_read_en),
      .spi_read_data    (spi_read_data),
      .spi_read_done    (spi_read_done)
    );
  
  pp pp_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .pp_en            (pp_en),
      .pp_done          (pp_done),
      .wr_fifo_rd       (wr_fifo_rd),
      .wrdata           (wrdata),
      .wr_len           (wr_len),
      .wr_addr          (wr_addr),
      
      .pp_send_en       (pp_send_en),
      .pp_send_data     (pp_send_data),
      .spi_send_done    (spi_send_done)
    );
  
  se se_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .se_en            (se_en),
      .se_addr          (se_addr),
      .se_done          (se_done),
      
      .se_send_en       (se_send_en),
      .se_send_data     (se_send_data),
      .spi_send_done    (spi_send_done)
    );
  
  wren wren_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .wren_en          (wren_en),
      .wren_done        (wren_done),
      
      .wren_send_en     (wren_send_en),
      .wren_send_data   (wren_send_data),
      .spi_send_done    (spi_send_done)
    );

  be be_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .be_en            (be_en),
      .be_done          (be_done),
      
      .be_send_en       (be_send_en),
      .be_send_data     (be_send_data),
      .spi_send_done    (spi_send_done)
    );
  
  mux7_1 mux7_1_inst(

      .rdsr_send_en     (rdsr_send_en),
      .rdsr_send_data   (rdsr_send_data),
      .rdsr_read_en     (rdsr_read_en),
      
      .pp_send_en       (pp_send_en),
      .pp_send_data     (pp_send_data),
      
      .wren_send_en     (wren_send_en),
      .wren_send_data   (wren_send_data),
      
      .be_send_en       (be_send_en),
      .be_send_data     (be_send_data),
      
      .se_send_en       (se_send_en),
      .se_send_data     (se_send_data),
      
      .rdid_send_en     (rdid_send_en),
      .rdid_send_data   (rdid_send_data),
      .rdid_read_en     (rdid_read_en),
      
      .read_send_en     (read_send_en),
      .read_send_data   (read_send_data),
      .read_read_en     (read_read_en),
      
      .mux_sel          (mux_sel),
      
      .spi_send_en      (spi_send_en),
      .spi_send_data    (spi_send_data),
      .spi_read_en      (spi_read_en)
    );
  
  spi_8bit_drive spi_8bit_drive_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .spi_send_en      (spi_send_en),
      .spi_send_data    (spi_send_data),
      .spi_send_done    (spi_send_done),
      
      .spi_read_en      (spi_read_en),
      .spi_read_data    (spi_read_data),
      .spi_read_done    (spi_read_done),
      
      .spi_sclk         (spi_sclk),
      .spi_mosi         (spi_mosi),
      .spi_miso         (spi_miso)
  );
  
endmodule

RTL仿真

本次设计涉及到读取flash的id以及状态寄存器,所以在仿真时需要加入仿真模型。仿真模型放在msim的m25p16_sim_module中。m25p16为仿真模型的顶层文件。

由于读写和擦除的时间较长,RTL仿真中,将只仿真RDSR和RDID,其他的功能测试在板级测试时进行。

仿真代码如下:

代码语言:javascript复制
`timescale 1ns/1ps

module m25p16_drive_tb;

  reg             clk;
  reg             rst_n;
  
  wire            drive_busy;
  
  wire            spi_cs_n;
  wire            spi_sclk;
  wire            spi_mosi;
  wire            spi_miso;

  m25p16_drive m25p16_drive_inst(

      .clk              (clk),
      .rst_n            (rst_n),
      
      .wrfifo_wr        (1'b0),
      .wrfifo_data      (8'd0),
      
      .flag_be          (1'b0),
      .flag_se          (1'b0),
      .flag_wr          (1'b0),
      .flag_rd          (1'b0),
      
      .addr             (24'd0),
      .len              (9'd0),
      
      .rdfifo_rd        (1'b0),
      .rdfifo_rdata     (),
      .rdfifo_rdempty   (),
      
      .drive_busy       (drive_busy),
      
      .spi_cs_n         (spi_cs_n),
      .spi_sclk         (spi_sclk),
      .spi_mosi         (spi_mosi),
      .spi_miso         (spi_miso)
    );

  m25p16 m25p16_inst(
      .c                (spi_sclk),
      .data_in          (spi_mosi),
      .s                (spi_cs_n),
      .w                (1'b1),
      .hold             (1'b1),
      .data_out         (spi_miso)
    );

  initial clk = 1'b0;
  always # 50 clk = ~clk;
  
  initial begin
    rst_n = 1'b0;
    # 201
    rst_n = 1'b1;
    @ (negedge drive_busy);
    # 2000
    $stop;
  end

endmodule

在设置testbench时,注意将所有文件全部添加到文件中。

添加描述

选择testbench时,注意选中设置的m25p16_drive_tb。

添加描述

利用modelsim仿真,可以得出如下RTL仿真波形。

添加描述

读到ID,以及检测WIP都是正确的。

板级测试

由于m25p16的时序原因,整个设计工作在10MHz(利用PLL产生)。

在进行测试控制时,对最后一个扇区进行擦除;对最后一个扇区的第一页进行写入数据100个(1至100);对最后一个扇区的第一个进行读取,验证数据是否为1至100。

测试的控制模块命名为test_ctrl。

此模块采用状态机实现。WRFIFO(将1至100写入wrfifo中)、SE(扇区擦除)、PP(写入flash)、RD(读出flash)、WAIT_RD(等待读取)、CHECK( 检测读出的数据的正确性)。

添加描述

设计代码为:

代码语言:javascript复制
module test_ctrl (

  input     wire                  clk,
  input     wire                  rst_n,
  
  output    reg                   wrfifo_wr,
  output    reg     [7:0]         wrfifo_data,
  
  output    reg                   flag_se,
  output    reg                   flag_wr,
  output    reg                   flag_rd,
  
  input     wire                  drive_busy,
  
  output    reg                   rdfifo_rd,
  input     wire    [7:0]         rdfifo_rdata
);

  localparam      WRFIFO      =     6'b000_001;
  localparam      SE          =     6'b000_010;
  localparam      PP          =     6'b000_100;
  localparam      RD          =     6'b001_000;
  localparam      WAIT_RD     =     6'b010_000;
  localparam      CHECK       =     6'b100_000;
  
  reg               [5:0]         c_state;
  reg               [5:0]         n_state;
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      c_state <= WRFIFO;
    else
      c_state <= n_state;
  end
  
  always @ * begin
    case (c_state)
      WRFIFO        :   begin
        if (wrfifo_data == 8'd100)
          n_state = SE;
        else
          n_state = WRFIFO;
      end
      
      SE            :   begin
        if (drive_busy == 1'b0)
          n_state = PP;
        else
          n_state = SE;
      end
      
      PP            :   begin
        if (drive_busy == 1'b0)
          n_state = RD;
        else
          n_state = PP;
      end
      
      RD            :   begin
        if (drive_busy == 1'b0)
          n_state = WAIT_RD;
        else
          n_state = RD;
      end
    
      WAIT_RD       :   begin
        if (drive_busy == 1'b0)
          n_state = CHECK;
        else
          n_state = WAIT_RD;
      end

      CHECK         :   begin
        n_state = CHECK;
      end

      default       :   n_state = WRFIFO;
      
    endcase
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wrfifo_data <= 8'd0;
    else
      if (c_state == WRFIFO && wrfifo_data < 8'd100)
        wrfifo_data <= wrfifo_data   1'b1;
      else
        wrfifo_data <= 8'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      wrfifo_wr <= 1'd0;
    else
      if (c_state == WRFIFO && wrfifo_data < 8'd100)
        wrfifo_wr <= 1'd1;
      else
        wrfifo_wr <= 1'd0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      flag_se <= 1'b0;
    else
      if (c_state == SE && drive_busy == 1'b0)
        flag_se <= 1'b1;
      else
        flag_se <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      flag_wr <= 1'b0;
    else
      if (c_state == PP && drive_busy == 1'b0)
        flag_wr <= 1'b1;
      else  
        flag_wr <= 1'b0;
  end

  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      flag_rd <= 1'b0;
    else
      if (c_state == RD && drive_busy == 1'b0)
        flag_rd <= 1'b1;
      else
        flag_rd <= 1'b0;
  end
  
  always @ (posedge clk, negedge rst_n) begin
    if (rst_n == 1'b0)
      rdfifo_rd <= 1'b0;
    else
      if (c_state == WAIT_RD && drive_busy == 1'b0)
        rdfifo_rd <= 1'b1;
      else
        if (c_state == CHECK && rdfifo_rdata == 8'd99)
          rdfifo_rd <= 1'b0;
        else  
          rdfifo_rd <= rdfifo_rd;
  end
  
endmodule

将test模块设置为顶层。在test模块中,m25p16_drive例化中,对于整片擦除不做控制,对于addr直接指向最后一个扇区的第一页,len指定为100。

代码为:

代码语言:javascript复制
module test (

  input     wire                  clk,
  input     wire                  rst_n,
  
  output    wire                  spi_cs_n,
  output    wire                  spi_sclk,
  output    wire                  spi_mosi,
  input     wire                  spi_miso
);

  wire                            wrfifo_wr;
  wire              [7:0]         wrfifo_data;
  wire                            flag_rd;
  wire                            flag_se;
  wire                            flag_wr;
  wire                            drive_busy;
  wire                            rdfifo_rd;
  wire              [7:0]         rdfifo_rdata;
  
  wire                            clk_10m;
  wire                            pll_locked;
  
  pll_test  pll_test_inst (
      .areset             ( ~rst_n ),
      .inclk0             ( clk ),
      .c0                 ( clk_10m ),
      .locked             ( pll_locked )
    );

  test_ctrl test_ctrl_inst(

      .clk                (clk_10m),
      .rst_n              (pll_locked),
      
      .wrfifo_wr          (wrfifo_wr),
      .wrfifo_data        (wrfifo_data),
      
      .flag_se            (flag_se),
      .flag_wr            (flag_wr),
      .flag_rd            (flag_rd),
      
      .drive_busy         (drive_busy),
      
      .rdfifo_rd          (rdfifo_rd),
      .rdfifo_rdata       (rdfifo_rdata)
    );
    
  m25p16_drive m25p16_drive_inst(

      .clk              (clk_10m),
      .rst_n            (pll_locked),
      
      .wrfifo_wr        (wrfifo_wr),
      .wrfifo_data      (wrfifo_data),
      
      .flag_be          (1'b0),
      .flag_se          (flag_se),
      .flag_wr          (flag_wr),
      .flag_rd          (flag_rd),
      
      .addr             (24'hff0000),
      .len              (9'd100),
      
      .rdfifo_rd        (rdfifo_rd),
      .rdfifo_rdata     (rdfifo_rdata),
      .rdfifo_rdempty   (),
      
      .drive_busy       (drive_busy),
      
      .spi_cs_n         (spi_cs_n),
      .spi_sclk         (spi_sclk),
      .spi_mosi         (spi_mosi),
      .spi_miso         (spi_miso)
    );
  
endmodule

由于开发板上的flash是为FPGA进行保存配置信息的,所以管脚都连接在专用管脚上,本次实验需要将这专用管脚配置为普通io。

右击器件型号,选择device。

添加描述

点击device and pin options。

添加描述

选择Dual-purpose pins,将其中所有的功能改为普通IO。

添加描述

点击ok后,即可进行综合分析。

连接开发板和PC,打开逻辑分析仪。

采样时钟选择10MHz(PLL 的c0),采样深度设置为2K。

添加描述

观测信号如下图所示。

添加描述

首先将wrfifo_wr的触发条件设置为上升沿。点击触发后,按下复位按键。触发后,可以看到写入数据1至100后,然后进行SE命令。

添加描述

将rdfifo_rd的触发条件设置为上升沿(将wrfifo_wr触发条件修改为donot care)。点击触发后,按下复位按键。

添加描述

通过仿真和下板实测,验证控制器设计正确。

0 人点赞