• 【Verilog实战】SPI协议底层硬件接口设计和功能验证(附源码)


    完整程序:点击下载
    脚  本:makefile
    工  具:vcs 和 verdi


    路 线:



    一、SPI协议

    1.1 概念

    SPI 协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。和UART协议类似,都属于片外协议,但UART协议是通用的异步串行通信接口。


    1.2 物理层

    在这里插入图片描述

      SPI 协议使用 3 条总线及N条片选线,3 条总线分别为 SCLK、MOSI、MISO,片选线为CS,其中3条总线是多个从设备共用的,cs是每一个从机有一条。它们的作用介绍如下:


    (1)CS
      全称:Chip Selection,从设备选择信号线,常称为片选信号线,也称为 NSS、SS。 SPI 协议中没有设备地址,它使用 cs 信号线来寻址,当主机要选择从设备时,把该从设备的 cs 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI通讯以 cs 线置低电平为开始信号,以 cs 线被拉高作为结束信号


    (2)SCLK
      全称:Serial Clock。时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,两个设备之间通讯时,通讯速率受限于低速设备。


    (3)MOSI
      全称:Master Output, Slave Input。主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。


    (4)MISO
      全称:Master Input,Slave Output。主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。


    1.3 协议层

    (1)通信模式

    SPI工作模式CPOLCPHA空闲时的SCLK电平采样时刻
    000低电平奇数边沿
    101低电平偶数边沿
    210高电平奇数边沿
    311高电平偶数边沿

    SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。工作模式由“时钟极性 CPOL”和“时钟相位 CPHA”决定。

    • 时钟极性 CPOL: 表示SPI 通讯设备处于空闲状态时,SCK信号线的电平信号(即 SPI 通讯开始前、CS 线为高电平时 SCLK的状态)。CPOL=0时, SCK在空闲状态时为低电平,CPOL=1 时,则相反。
    • 时钟相位 CPHA:表示数据采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCLK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCLK 的“偶数边沿”采样。

    (2)时序

    根据 SCLK 在空闲状态时的电平,分为两种情况。CPOL=0时,SCLK信号线在空闲状态为低电平;CPOL=1时,空闲状态为高电平。

    在这里插入图片描述

    无论 CPOL=0,还是=1,因为配置的时钟相位 CPHA=0,在图中可以看到,采样时刻都是在 SCLK 的奇数边沿。注意当 CPOL=0 的时候,时钟的奇数边沿是上升沿,而 CPOL=1 的时候,时钟的奇数边沿是下降沿。所以 SPI 的采样时刻不是由上升/下降沿决定的


    在这里插入图片描述

    类似地,当 CPHA=1 时,不受 CPOL 的影响,数据信号在 SCK 的偶数边沿被采样。


    (3)基本通信过程
    在这里插入图片描述

    • 起始和结束信号
      标号1处,CS 信号线(NNS)由高变低,是 SPI 通讯的起始信号。CS 信号线(NNS) 是每个从机各自独占的信号线,当从机在自己的片选信号线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。标号6处,CS 信号线(NNS) 由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

    • 数据有效性
      SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB先行或 LSB先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。

      图中的2~5标号处,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK的下降沿时被采样。即在 SCK的下降沿时刻,MOSI 及 MISO的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效。
    注意:SPI 每次数据的单位数不受限制


    二、Spec

    2.1 Function descripton

      上游模块实现通过SPI接口与下游进行并行数据的交互,完成了并串互转和串行数据传输与采集的工作。


    2.2 Feature list

    1. 接收上一级模块并行数据,将数据按照SPI协议发送出去
    2. SCLK采用10MHz
    3. 模块工作时钟100MHz
    4. 当接收到的串行数据做串并转换,将并行数据反馈给上一级模块
    5. 规定SPI接口工作在模式0,即CPOL时钟极性为0,CPHA时钟相位为0。
    6. 低位数据LSB先行

    2.3 Block Diagram

    在这里插入图片描述


    2.4 Interface description

    Signal NameWidthDirectionDescription
    clk_i1inputsystem clock,100MHz
    rst_n_i1inputsystem reset signal
    cmd_in12input输入命令,[11]: write/read;
    [10:8]: addr;
    [7:0]: 写数据 or 无意义
    cmd_rdy1output输入命令ready
    cmd_vld_i1input输入命令valid
    sclk1outputspi时钟,10MHz
    cs1outputspi片选
    mosi1outputspi串行数据,master发,slave收
    miso1inputspi串行数据,master收,slave发
    read_vld_o1output读数据valid
    read_data_o8output读数据

    2.5 Timing

    (1)Write timing
    在这里插入图片描述


    (2)Read Timing
    在这里插入图片描述


    三、Design and Verification

    3.1 RTL

    /*-- modified by xlinxdu, 2022/05/20
      -- clk_i : 100MHz
      -- sclk_o: 10MHz
      -- sclk  : 0(CPOL)
      -- mosi  : posedge sclk(CPHA)
      -- receive_en:receive signal
      -- cmd_in = 12bit;[11]:r/w;[10:8]:addr;[7:0]:data 
    */
    module spi 
    #(
      parameter CMD_RW_WIDTH    =  1,
      parameter CMD_ADDR_WIDTH  =  3,
      parameter CMD_DATA_WIDTH  =  8,
      parameter CMD_IN_WIDTH    =  CMD_RW_WIDTH + CMD_ADDR_WIDTH +
                                   CMD_DATA_WIDTH,
      parameter CLK_CNT_WIDTH   =  4,
      parameter BIT_CNT_WIDTH   =  4,
      parameter DLY_CNT_WIDTH   =  2,//delay counter
      parameter DLY_CNT         =  2,//delay counter of clk
      parameter FDC             =  10
    )(
    //-- system signal
      input clk_i  ,
      input rst_n_i,
    
    //-- mosi
      input       [CMD_IN_WIDTH-1:0]   cmd_in     ,
      input                            cmd_vld_i  ,
      output  reg                      cmd_rdy_o  ,
      output  reg                      sclk_o     ,
      output  reg                      cs_o       ,
      output  reg                      mosi_o     ,
    
    //-- miso
      input                            miso_i     ,
      output  reg                      read_vld_o ,
      output  reg [CMD_DATA_WIDTH-1:0] read_data_o
    );
    
    reg [CMD_IN_WIDTH-1 :0]  cmd_in_buf ;
    reg                      start_flag ;
    reg [CLK_CNT_WIDTH-1:0]  clk_cnt    ;
    reg [BIT_CNT_WIDTH-1:0]  bit_cnt    ;
    reg [DLY_CNT_WIDTH-1:0]  dly_cnt    ;
    reg                      dly_en     ;
    
    reg [CMD_ADDR_WIDTH-1:0] rd_data_buf;
    
    wire wr_end_flag ;
    wire rd_stop_flag;
    wire rd_end_flag ;
    
    reg  receive_en  ;
    
    /*-----------------------------------------------\
     --             updata cmd_rdy_o              --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        cmd_rdy_o <= 1'b1;
      end
      else if (cmd_vld_i) begin
        cmd_rdy_o <= 1'b0;
      end
      else if(wr_end_flag || read_vld_o) begin
        cmd_rdy_o <= 1'b1;
      end
    end
    
    /*-----------------------------------------------\
     --            updata cmd_in_buf              --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        cmd_in_buf <= {(CMD_IN_WIDTH){1'b0}};
      end
      else if (cmd_vld_i) begin
        cmd_in_buf <= cmd_in;
      end
    end
    
    /*-----------------------------------------------\
     --    Generate a flag to start the transfer  --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        start_flag <= 1'b0;
      end
      else if (cmd_vld_i) begin
        start_flag <= 1'b1;
      end
      else begin
        start_flag <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --                Chip Selection              --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        cs_o <= 1'b1;
      end
      else if (start_flag) begin
        cs_o <= 1'b0;
      end
      else if(dly_en && (dly_cnt == (DLY_CNT-1)))begin
        cs_o <= 1'b0;
      end
      else if(wr_end_flag || rd_stop_flag) begin
        cs_o <= 1'b1;
      end
    end
    
    /*-----------------------------------------------\
     --              Frequency divider             --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        clk_cnt <= {(CLK_CNT_WIDTH){1'b0}};
      end
      else if (clk_cnt == (FDC-1)) begin
        clk_cnt <= {(CLK_CNT_WIDTH){1'b0}};
      end
      else if(!cs_o) begin
        clk_cnt <= clk_cnt + 1'b1;
      end
    end
    
    /*-----------------------------------------------\
     --                 updata sclk                --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        sclk_o <= 1'b0;
      end
      else if(clk_cnt == (FDC/2-1)) begin
        sclk_o <= 1'b1;
      end
      else if(clk_cnt == (FDC-1)) begin
        sclk_o <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --  Generate of write end flag ,read stop flag
         and read end flag                         --
    \-----------------------------------------------*/
    assign wr_end_flag = (!cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && (bit_cnt == (CMD_IN_WIDTH-1)));
    assign rd_stop_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && (bit_cnt == (CMD_RW_WIDTH+CMD_ADDR_WIDTH-1)));
    assign rd_end_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && (bit_cnt == (CMD_DATA_WIDTH-1)));
    
    /*-----------------------------------------------\
     --                 bit counter                --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        bit_cnt <= {(BIT_CNT_WIDTH){1'b0}};
      end
      else if(wr_end_flag)begin
        bit_cnt <= {(BIT_CNT_WIDTH){1'b0}};
      end
      else if(!receive_en && rd_stop_flag)begin
        bit_cnt <= {(BIT_CNT_WIDTH){1'b0}}; 
      end
      else if(receive_en && rd_end_flag)begin
        bit_cnt <= {(BIT_CNT_WIDTH){1'b0}}; 
      end
      else if (!cs_o && clk_cnt == (FDC-1)) begin
        bit_cnt <= bit_cnt + 1'b1;
      end
    end
    
    /*-----------------------------------------------\
     --              updata MOSI                   --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        mosi_o <= 1'b0;
      end
      else if (!cs_o) begin
        if(!cmd_in_buf[CMD_IN_WIDTH-1])begin
          mosi_o <= cmd_in_buf[bit_cnt];
        end
        else begin
          mosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];
        end
      end
      else begin
        mosi_o <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --                 delay enable               --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        dly_en <= 1'b0;
      end
      else if (rd_stop_flag) begin
        dly_en <= 1'b1;
      end
      else if(dly_cnt == (DLY_CNT-1))begin
        dly_en <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --                delay counter               --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
      else if (dly_en) begin
        dly_cnt <= dly_cnt + 1'b1;
      end
      else begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
    end
    
    /*-----------------------------------------------\
     --               receive enable               --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        receive_en <= 1'b0;
      end
      else if(dly_cnt == (DLY_CNT-1)) begin
        receive_en <= 1'b1;
      end
      else if(rd_end_flag) begin
        receive_en <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --         updata rd_data_buf of miso         --
    \-----------------------------------------------*/
    always @ (posedge sclk_o or negedge rst_n_i) begin
      if (!rst_n_i) begin
        rd_data_buf <= {(CMD_DATA_WIDTH){1'b0}};
      end
      else if (receive_en) begin
        rd_data_buf[bit_cnt] <= miso_i; 
      end
    end
    
    /*-----------------------------------------------\
     --               updata read valid           --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        read_vld_o <= 1'b0;
      end
      else if (rd_end_flag) begin
        read_vld_o <= 1'b1;
      end
      else begin
        read_vld_o <= 1'b0;
      end
    end
    
    /*-----------------------------------------------\
     --              updata read_data_o            --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        read_data_o <= {(CMD_DATA_WIDTH){1'b0}};
      end
      else if (read_vld_o) begin
        read_data_o <= rd_data_buf;
      end
    end
    
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279

    3.2 Test bench

    `timescale 1ns/1ps
    module tb_spi ;
      reg        clk_i    ;
      reg        rst_n_i  ;
    
      reg [11:0] cmd_in   ;
      reg        cmd_vld_i;
      wire       cmd_rdy_o;
      wire       sclk_o   ;
      wire       cs_o     ;
      wire       mosi_o   ;
    
      reg        miso_i     ;
      wire       read_vld_o ;
      wire [7:0] read_data_o;
    
    initial begin
      clk_i = 0;
    end
    
    always #5 clk_i = ~clk_i;
    
    initial begin
      rst_n_i    = 1;
      cmd_vld_i  = 0;
      cmd_in = 12'b0011_0001_1001;//16'h318
      #5 rst_n_i = 0;
      #5 rst_n_i = 1;
         cmd_vld_i=1;
      #10 cmd_vld_i =0;
      cmd_in = 12'b1010_0011_0010;//16'hA32
      #1500 ;
          cmd_vld_i=1;
      #10 cmd_vld_i =0;
      
    end
    
    initial begin
      miso_i = 0;
      #1955 miso_i = 1;
      #100 miso_i = 0;
      #100 miso_i = 1;
      #100 miso_i = 0;
    
      #100 miso_i = 1;
      #100 miso_i = 1;
      #100 miso_i = 0;
      #100 miso_i = 1;
      
      #100;
    end
    
    spi tb_spi(
              .clk_i      (clk_i      ),
              .rst_n_i    (rst_n_i    ),
              .cmd_in     (cmd_in     ),
              .cmd_vld_i  (cmd_vld_i  ),
              .cmd_rdy_o  (cmd_rdy_o  ),
              .sclk_o     (sclk_o     ),
              .cs_o       (cs_o       ),
              .mosi_o     (mosi_o     ),
              .miso_i     (miso_i     ),
              .read_vld_o (read_vld_o ),
              .read_data_o(read_data_o)
              );
    
    initial begin
      #4500 $finish            ;
      $fsdbDumpfile("spi.fsdb");
      $fsdbDumpvars            ;
      $fsdbDumpMDA             ;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    3.3 Analyse

    bug1:在进行读操作时,mosi发送完读写指令+读地址(标号1阶段)后,又重复发送了一遍(标号2阶段),标号3阶段出现了不定态。

    在这里插入图片描述

    定位到更新mosi代码块

    /*-----------------------------------------------\
     --              updata MOSI                   --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        mosi_o <= 1'b0;
      end
      else if (!cs_o) begin
        if(!cmd_in_buf[CMD_IN_WIDTH-1])begin
          mosi_o <= cmd_in_buf[bit_cnt];
        end
        else begin
          mosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];
        end
      end
      else begin
        mosi_o <= 1'b0;
      end
    end
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    分析:由代码块可以知道,在读操作阶段,因为不是写就会不断执行,并且出现不定态的原因是在读操作中,在cs有效阶段(cs==0),会有两次cs拉低,第一次是发送读写控制位+读地址位,此时bit_cnt计数到3(0~3);第二次拉低是因为miso要反馈一个8位的串行数据流,此时,mosi应该不输出,逻辑错误。

    /*-----------------------------------------------\
     --              updata MOSI                   --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        mosi_o <= 1'b0;
      end
      else if (!cs_o) begin
        if(!cmd_in_buf[CMD_IN_WIDTH-1])begin
          mosi_o <= cmd_in_buf[bit_cnt];
        end
        else if(!receive_en)begin
          mosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];
        end
      end
      else begin
        mosi_o <= 1'b0;
      end
    end
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    更正:把读阶段的mosi触发条件改为只有非反馈数据阶段触发,即receive_en==0时触发。


    bug2:在miso反馈数据阶段,cs_o片选信号被中断过,导致cs_o被中断是因为产生了一个在读操作中,发送读写控制位+读地址后才会产生的高电平信号(rd_stop_flag)。这个rd_stop_flag是用来指示在读操作中,mosi已经发送完读写控制位和读地址位。

    在这里插入图片描述

    定位到rd_stop_flag更新代码块

    assign rd_stop_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && 
    					  (bit_cnt == (CMD_RW_WIDTH+CMD_ADDR_WIDTH-1)));
    
    
    • 1
    • 2
    • 3

    更改触发条件,只在miso非反馈数据阶段触发。

    assign rd_stop_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && 
    					  (bit_cnt == (CMD_RW_WIDTH+CMD_ADDR_WIDTH-1)) && !receive_en);
    
    
    • 1
    • 2
    • 3

    在这里插入图片描述


    bug3:读出数据缓存只有3位,而本来读出数据应该是8位

    在这里插入图片描述

    定位,发现是在定义的时候,用错参数

    reg [CMD_ADDR_WIDTH-1:0] rd_data_buf;
    
    • 1

    更改

    reg [CMD_DATA_WIDTH-1:0] rd_data_buf;
    
    • 1

    在这里插入图片描述


    bug4:mosi输出延迟了一拍

    在这里插入图片描述

    定位到mosi更新代码块

    /*-----------------------------------------------\
     --              updata MOSI                   --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        mosi_o <= 1'b0;
      end
      else if (!cs_o) begin
        if(!cmd_in_buf[CMD_IN_WIDTH-1])begin
          mosi_o <= cmd_in_buf[bit_cnt];
        end
        else if(!receive_en)begin
          mosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];
        end
      end
      else begin
        mosi_o <= 1'b0;
      end
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    分析:由代码块可以看到,mosi的更新是在cs片选信号拉低之后,再更新,但实际应该是mosi和cs同一时刻开始更新,逻辑错误。
    更改:引入一个mosi的读写使能信号rw_en,在cmd_vld_i有效时拉高,依此作为mosi输出的触发条件,即可完成与cs同步输出。
    在这里插入图片描述


    四、Result

    4.1 Write

    在这里插入图片描述

    在cmd_vld_i有效时,更新待发数据缓存cmd_in_buf,拉低cmd_rdy,并且生成一个写操作开始发送标志,打开发送使能,下一个时钟周期开始传输12bit数据,低位数据先行。在发完的前一个时钟周期生成结束写操作发送标志,并且拉高cmd_rdy信号。


    4.2 Read

    在这里插入图片描述

    读操作,在cmd_vld有效时,将待发数据写入缓存,之后开始发送读写控制位和地址位,一共4bit数据。发送结束前一周期生成rd_stop_flag信号,并开始启动延时,等待从模块反馈数据。等待时间事先已规定好,这里是两个时钟周期,并且可以配置。延时结束后,开始通过sclk采集miso反馈的数据,一共8bit,采集完成后,生成一个read_vld高电平,通过这个高电平,更新read_data_o。


    4.3 Output

    在这里插入图片描述

    仿真时序和规定时序在两个cmd_in之后不一致,功能验证不通过

    4.4 bug(补充)

    补充bug1:在dly_cnt中,多记数一个,造成不必要的信号变化,定位到delay counter代码块
    在这里插入图片描述

    /*-----------------------------------------------\
     --                delay counter               --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
      else if (dly_en) begin
        dly_cnt <= dly_cnt + 1'b1;
      end
      else begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
    end
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    更改清零的优先级。

    /*-----------------------------------------------\
     --                delay counter               --
    \-----------------------------------------------*/
    always @ (posedge clk_i or negedge rst_n_i) begin
      if (!rst_n_i) begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
      else if(dly_cnt == (DLY_CNT-1)) begin
        dly_cnt <= {(DLY_CNT_WIDTH){1'b0}};
      end
      else if (dly_en) begin
        dly_cnt <= dly_cnt + 1'b1;
      end
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    补充bug2:在没有新的数据来时,即传输完两次后,没新的cmd_vld来临,可是模块miso还在工作,延时计数器也打开了,经过分析,cs_o被拉低了,cs_o受dly_en和dly_cnt共同影响,而影响dly_en的是rd_stop_flag信号。因此更改这几个标志信号,使其在cmd_rdy_o低电平时间段才可以产生,而不是全时间段不断产生。

    /*-----------------------------------------------\
     --  Generate of write end flag ,read stop flag
         and read end flag                         --
    \-----------------------------------------------*/
    assign wr_end_flag  = (!cmd_in_buf[CMD_IN_WIDTH-1]) &&  (clk_cnt == (FDC-1) && (bit_cnt == (CMD_IN_WIDTH-1)));
    assign rd_stop_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) &&  (clk_cnt == (FDC-1) && 
                          (bit_cnt == (CMD_RW_WIDTH+CMD_ADDR_WIDTH-1)) && !receive_en);
    assign rd_end_flag  = (cmd_in_buf[CMD_IN_WIDTH-1]) && (clk_cnt == (FDC-1) && (bit_cnt == (CMD_DATA_WIDTH-1)));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    更改为:

    /*-----------------------------------------------\
     --  Generate of write end flag ,read stop flag
         and read end flag                         --
    \-----------------------------------------------*/
    assign wr_end_flag  = (!cmd_in_buf[CMD_IN_WIDTH-1]) && (!cmd_rdy_o) && 
    										(clk_cnt == (FDC-1) && (bit_cnt == (CMD_IN_WIDTH-1)));
    assign rd_stop_flag = (cmd_in_buf[CMD_IN_WIDTH-1]) && (!cmd_rdy_o) && (clk_cnt == (FDC-1) && 
                          (bit_cnt == (CMD_RW_WIDTH+CMD_ADDR_WIDTH-1)) && !receive_en);
    assign rd_end_flag  = (cmd_in_buf[CMD_IN_WIDTH-1]) && (!cmd_rdy_o) &&
    									  (clk_cnt == (FDC-1) && (bit_cnt == (CMD_DATA_WIDTH-1)));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    更改后,测试仿真时序和原来时序一致,功能验证通过。


    作者:xlinxdu
    版权:本文版权归作者所有
    转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。

  • 相关阅读:
    C++中(封闭)类的定义及使用特性---知识要点篇1
    面向对象实验一 类的建立与应用
    Baumer工业相机堡盟工业相机如何通过NEOAPISDK实现根据每次触发信号移动感兴趣区域ROI(C#)
    2.在码云上创建仓库,拉取到本地IDEA,修改项目并提交到仓库
    react antd实现upload上传文件前form校验,同时请求带data
    从Prefetch到Stream:重构v1.0代码库中的流式请求问题与解决方案
    嵌入式Linux驱动开发 02:将驱动程序添加到内核中
    【Vue3】动态 class 类
    .Net Core 3.1 解决数据大小限制
    Redis缓存击穿问题及解决思路
  • 原文地址:https://blog.csdn.net/qq_43244515/article/details/124919979