脚  本:makefile
工  具:vcs 和 verdi


写在前面

  1. 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是调试前的代码,方便bug重现。
  2. 调试后的程序提供下载,【下载地址】
  3. 发现了一个Verilog宝藏刷题网站,网站提供在线仿真环境(点击直达),同时新开了一个<刷题记录>专栏,持续打卡中…

路 线:

  • 【verilog实战】同步FIFO设计(附源码RTL/TB)
  • 【Verilog实战】异步FIFO设计(附源码RTL/TB)
  • 【Verilog实战】UART通信协议,半双工通信方式(附源码RTL/TB)
  • 【Verilog实战】SPI协议接口设计(附源码RTL/TB)
  • 【Verilog实战】AMBA 3 APB接口设计(附源码RTL/TB)
  • 【Verilog实战】AMBA AHB接口设计(附源码RTL/TB)
  • 【Verilog实战】AMBA AXI接口设计(附源码RTL/TB)
  • 【Verilog实战】UART2APB bridge 设计(附源码RTL/TB)
  • 【Verilog实战】AHB2APB bridge 设计(附源码RTL/TB)

文章目录

  • 一、SPI协议
    • 1.1 概念
    • 1.2 物理层
    • 1.3 协议层
  • 二、Spec
    • 2.1 Function descripton
    • 2.2 Feature list
    • 2.3 Block Diagram
    • 2.4 Interface description
    • 2.5 Timing
  • 三、Design and Verification
    • 3.1 RTL
    • 3.2 Test bench
    • 3.3 Analyse
  • 四、Result
    • 4.1 Write
    • 4.2 Read
    • 4.3 Output
    • 4.4 bug(补充)
  • ✍✍☛ [题库入口](https://www.nowcoder.com/link/pc_csdncpt_xlin_verilog)

一、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工作模式 CPOL CPHA 空闲时的SCLK电平 采样时刻
0 0 0 低电平 奇数边沿
1 0 1 低电平 偶数边沿
2 1 0 高电平 奇数边沿
3 1 1 高电平 偶数边沿

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 Name Width Direction Description
clk_i 1 input system clock,100MHz
rst_n_i 1 input system reset signal
cmd_in 12 input 输入命令,[11]: write/read;
[10:8]: addr;
[7:0]: 写数据 or 无意义
cmd_rdy 1 output 输入命令ready
cmd_vld_i 1 input 输入命令valid
sclk 1 output spi时钟,10MHz
cs 1 output spi片选
mosi 1 output spi串行数据,master发,slave收
miso 1 input spi串行数据,master收,slave发
read_vld_o 1 output 读数据valid
read_data_o 8 output 读数据

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 counterparameter DLY_CNT         =  2,//delay counter of clkparameter FDC             =  10
)(
//-- system signalinput clk_i  ,input rst_n_i,//-- mosiinput       [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     ,//-- misoinput                            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) beginif (!rst_n_i) begincmd_rdy_o <= 1'b1;endelse if (cmd_vld_i) begincmd_rdy_o <= 1'b0;endelse if(wr_end_flag || read_vld_o) begincmd_rdy_o <= 1'b1;end
end/*-----------------------------------------------\--            updata cmd_in_buf              --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begincmd_in_buf <= {(CMD_IN_WIDTH){1'b0}};endelse if (cmd_vld_i) begincmd_in_buf <= cmd_in;end
end/*-----------------------------------------------\--    Generate a flag to start the transfer  --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginstart_flag <= 1'b0;endelse if (cmd_vld_i) beginstart_flag <= 1'b1;endelse beginstart_flag <= 1'b0;end
end/*-----------------------------------------------\--                Chip Selection              --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begincs_o <= 1'b1;endelse if (start_flag) begincs_o <= 1'b0;endelse if(dly_en && (dly_cnt == (DLY_CNT-1)))begincs_o <= 1'b0;endelse if(wr_end_flag || rd_stop_flag) begincs_o <= 1'b1;end
end/*-----------------------------------------------\--              Frequency divider             --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginclk_cnt <= {(CLK_CNT_WIDTH){1'b0}};endelse if (clk_cnt == (FDC-1)) beginclk_cnt <= {(CLK_CNT_WIDTH){1'b0}};endelse if(!cs_o) beginclk_cnt <= clk_cnt + 1'b1;end
end/*-----------------------------------------------\--                 updata sclk                --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginsclk_o <= 1'b0;endelse if(clk_cnt == (FDC/2-1)) beginsclk_o <= 1'b1;endelse if(clk_cnt == (FDC-1)) beginsclk_o <= 1'b0;end
end/*-----------------------------------------------\--  Generate of write end flag ,read stop flagand 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) beginif (!rst_n_i) beginbit_cnt <= {(BIT_CNT_WIDTH){1'b0}};endelse if(wr_end_flag)beginbit_cnt <= {(BIT_CNT_WIDTH){1'b0}};endelse if(!receive_en && rd_stop_flag)beginbit_cnt <= {(BIT_CNT_WIDTH){1'b0}}; endelse if(receive_en && rd_end_flag)beginbit_cnt <= {(BIT_CNT_WIDTH){1'b0}}; endelse if (!cs_o && clk_cnt == (FDC-1)) beginbit_cnt <= bit_cnt + 1'b1;end
end/*-----------------------------------------------\--              updata MOSI                   --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginmosi_o <= 1'b0;endelse if (!cs_o) beginif(!cmd_in_buf[CMD_IN_WIDTH-1])beginmosi_o <= cmd_in_buf[bit_cnt];endelse beginmosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];endendelse beginmosi_o <= 1'b0;end
end/*-----------------------------------------------\--                 delay enable               --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindly_en <= 1'b0;endelse if (rd_stop_flag) begindly_en <= 1'b1;endelse if(dly_cnt == (DLY_CNT-1))begindly_en <= 1'b0;end
end/*-----------------------------------------------\--                delay counter               --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};endelse if (dly_en) begindly_cnt <= dly_cnt + 1'b1;endelse begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};end
end/*-----------------------------------------------\--               receive enable               --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginreceive_en <= 1'b0;endelse if(dly_cnt == (DLY_CNT-1)) beginreceive_en <= 1'b1;endelse if(rd_end_flag) beginreceive_en <= 1'b0;end
end/*-----------------------------------------------\--         updata rd_data_buf of miso         --
\-----------------------------------------------*/
always @ (posedge sclk_o or negedge rst_n_i) beginif (!rst_n_i) beginrd_data_buf <= {(CMD_DATA_WIDTH){1'b0}};endelse if (receive_en) beginrd_data_buf[bit_cnt] <= miso_i; end
end/*-----------------------------------------------\--               updata read valid           --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginread_vld_o <= 1'b0;endelse if (rd_end_flag) beginread_vld_o <= 1'b1;endelse beginread_vld_o <= 1'b0;end
end/*-----------------------------------------------\--              updata read_data_o            --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginread_data_o <= {(CMD_DATA_WIDTH){1'b0}};endelse if (read_vld_o) beginread_data_o <= rd_data_buf;end
endendmodule

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 beginclk_i = 0;
endalways #5 clk_i = ~clk_i;initial beginrst_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;endinitial beginmiso_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;
endspi 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

3.3 Analyse

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

定位到更新mosi代码块

/*-----------------------------------------------\--              updata MOSI                   --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginmosi_o <= 1'b0;endelse if (!cs_o) beginif(!cmd_in_buf[CMD_IN_WIDTH-1])beginmosi_o <= cmd_in_buf[bit_cnt];endelse beginmosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];endendelse beginmosi_o <= 1'b0;end
end

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

/*-----------------------------------------------\--              updata MOSI                   --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginmosi_o <= 1'b0;endelse if (!cs_o) beginif(!cmd_in_buf[CMD_IN_WIDTH-1])beginmosi_o <= cmd_in_buf[bit_cnt];endelse if(!receive_en)beginmosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];endendelse beginmosi_o <= 1'b0;end
end

更正:把读阶段的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)));

更改触发条件,只在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);


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

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

reg [CMD_ADDR_WIDTH-1:0] rd_data_buf;

更改

reg [CMD_DATA_WIDTH-1:0] rd_data_buf;


bug4:mosi输出延迟了一拍

定位到mosi更新代码块

/*-----------------------------------------------\--              updata MOSI                   --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) beginmosi_o <= 1'b0;endelse if (!cs_o) beginif(!cmd_in_buf[CMD_IN_WIDTH-1])beginmosi_o <= cmd_in_buf[bit_cnt];endelse if(!receive_en)beginmosi_o <= cmd_in_buf[CMD_DATA_WIDTH + bit_cnt];endendelse beginmosi_o <= 1'b0;end
end

分析:由代码块可以看到,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) beginif (!rst_n_i) begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};endelse if (dly_en) begindly_cnt <= dly_cnt + 1'b1;endelse begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};end
end

更改清零的优先级。

/*-----------------------------------------------\--                delay counter               --
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};endelse if(dly_cnt == (DLY_CNT-1)) begindly_cnt <= {(DLY_CNT_WIDTH){1'b0}};endelse if (dly_en) begindly_cnt <= dly_cnt + 1'b1;end
end

补充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 flagand 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)));

更改为:

/*-----------------------------------------------\--  Generate of write end flag ,read stop flagand 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)));

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


✍✍☛ 题库入口

  经过一段时间的沉淀,发现入行IC行业,自己的底子还是很差,写的文章质量参差不齐,也没能解答大家的疑问。还是要实打实从基础学起,由浅入深。因此决定推倒重来,通过补充/完善基础知识的同时,通过题库刷题不断提高自己的设计水平,题库推荐给大家(点击直达),<题库记录>栏目不定期更新,欢迎前来讨论。2022.08.29 记


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

04【Verilog实战】SPI协议底层硬件接口设计(附源码RTL/TB)相关推荐

  1. 05【Verilog实战】AMBA 3 APB接口设计(附源码RTL/TB)

    官方手册:点击下载 脚  本:makefile 工  具:vcs & verdi 写在前面 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是调试前的代码,方便bug重现. 调试后的程序 ...

  2. 01 【Verilog实战】同步FIFO的设计(附源码RTL/TB)

    虚拟机:VMware -14.0.0.24051 环 境:ubuntu 18.04.1 脚 本:makefile(点击查看) 应用工具:vcs 和 verdi 写在前面 这个专栏的内容记录的是个人学习 ...

  3. 02【Verilog实战】异步FIFO设计(附源码RTL/TB)

    脚 本:makefile 工 具:vcs 和 verdi 文 章:1. 同步FIFO的设计和功能验证(附源码)     2. Verilog的亚稳态现象和跨时钟域处理方法 写在前面 这个专栏的内容记录 ...

  4. erlang底层c定时器设计-Erlang源码学习二

    Erlang底层的定时器实现位于源码的erts/emulator/beam/time.c文件,用时间轮的方式动态添加和删除定时器,结构体名为typedef struct ErtsTimerWheel_ ...

  5. 实战 | SpringBoot微信点餐系统(附源码)

    点击上方"java进阶架构师",选择右上角"置顶公众号" 20大进阶架构专题每日送达 架构 前后端分离: 补充: setting.xml 文件的作用:setti ...

  6. 【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo...

    开篇先给大家讲段历史故事,博主是湖北襄阳人.襄阳物华天宝,人杰地灵,曾用名襄樊.在2800多年的历史文化中出现了一代名相诸葛亮(卧龙),三国名士庞统(凤雏),魏晋隐士司马徽(水镜先生),唐代大诗人孟浩 ...

  7. C/C++项目实战:实现在线考试系统,附源码

    在线考试系统是国内市场占有率最高的通用化网络考试软件,经过数千家客户的长期实践检验,适用于需要开展网络考试测评的学校.企业.政府和军队,极大地降低客户的硬件投资成本. C++在线考试系统,Access ...

  8. 【Python爬虫】【2020最新哔哩哔哩验证码识别实战】【滑块验证码】【附源码】

    哔哩哔哩实战----验证码识别 利用Selenium来模拟B站登录,并实现线极验验证码的识别. 初步分析 B站的登录界面如下: 登录需要完成:1.账户输入:2.密码输入:3.登录后需要通过滑块来进行验 ...

  9. Verilog 实现数码管显视驱动【附源码】

    目录 1.实验平台 2.实验目的 2.1.实验内容 3.实验流程 3.1.实验原理 3.2.系统架构 3.3.功能模块划分 3.3.1.数据产生模块 模块框图 信号定义 设计文件 3.3.2.数码管驱 ...

最新文章

  1. 好奇,我们常用的 Integer 内部为什么会去实现 Comparable 接口,他的作用是什么?...
  2. Android客户端实现七牛云存储文件上传
  3. Springmvc的服务端数据验证-----Hibernate Validator
  4. RabbitMQ死信队列代码架构图
  5. cupsd进程_linux pstree命令显示正在运行的进程的进程树状图
  6. 聊天软件项目TCP升级版
  7. linux 配置内网yum源
  8. Java IO学习笔记(五):内存操作流
  9. YII学习笔记6.20日
  10. python13文件_python中13个实用的文件操作
  11. 走进小作坊(十四)----web2.0策略指南
  12. Arduino基础入门篇(认识开发板和面包板)
  13. CentOS的 Oracle 11g R2安装
  14. 吴恩达机器学习ex2 Logistic Regression (python)
  15. 分享一些实用的手机应用
  16. 微信公众号支付 使用基于thinkphp 使用微信官网的sdk
  17. 秘密是如何被泄露的?自建文件分享神器HFS
  18. Flutter中如何利用StreamBuilder和BLoC来控制Widget状态
  19. java查询数据库方法show create
  20. JS中预处理是一种好无节操的机制

热门文章

  1. oracle闪回ddl,Oracle闪回详解
  2. 切边压力机行业调研报告 - 市场现状分析与发展前景预测
  3. MarkdownPad 2使用教程(语法篇)
  4. Filter过滤器讲解
  5. Lession11 集合和泛型(ArrayList方法、Arraylist类、ArrayList添加对象、ArrayList长度、HashTable类、Hashtable类练习-----)
  6. python图片转文字easyocr_Python OpenCV读取png图像转成jpg图像存储
  7. win10照片查看器恢复办法
  8. (个体户)注册公众平台步骤
  9. 买二手房不后悔先知道8件事 高楼层未必卖得好
  10. 赵雅智_名片夹(4)_Android中listview可折叠伸缩仿手风琴效果(静态)