04【Verilog实战】SPI协议底层硬件接口设计(附源码RTL/TB)
脚 本:makefile
工 具:vcs 和 verdi
写在前面
- 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是
调试前
的代码,方便bug重现。调试后
的程序提供下载,【下载地址】- 发现了一个
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
- 接收上一级模块并行数据,将数据按照SPI协议发送出去
- SCLK采用10MHz
- 模块工作时钟100MHz
- 当接收到的串行数据做串并转换,将并行数据反馈给上一级模块
- 规定SPI接口工作在模式0,即CPOL时钟极性为0,CPHA时钟相位为0。
- 低位数据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)));
更改后,测试仿真时序和原来时序一致,功能验证通过。
✍✍☛ 题库入口
作者:xlinxdu
版权:本文版权归作者所有
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。
04【Verilog实战】SPI协议底层硬件接口设计(附源码RTL/TB)相关推荐
- 05【Verilog实战】AMBA 3 APB接口设计(附源码RTL/TB)
官方手册:点击下载 脚 本:makefile 工 具:vcs & verdi 写在前面 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是调试前的代码,方便bug重现. 调试后的程序 ...
- 01 【Verilog实战】同步FIFO的设计(附源码RTL/TB)
虚拟机:VMware -14.0.0.24051 环 境:ubuntu 18.04.1 脚 本:makefile(点击查看) 应用工具:vcs 和 verdi 写在前面 这个专栏的内容记录的是个人学习 ...
- 02【Verilog实战】异步FIFO设计(附源码RTL/TB)
脚 本:makefile 工 具:vcs 和 verdi 文 章:1. 同步FIFO的设计和功能验证(附源码) 2. Verilog的亚稳态现象和跨时钟域处理方法 写在前面 这个专栏的内容记录 ...
- erlang底层c定时器设计-Erlang源码学习二
Erlang底层的定时器实现位于源码的erts/emulator/beam/time.c文件,用时间轮的方式动态添加和删除定时器,结构体名为typedef struct ErtsTimerWheel_ ...
- 实战 | SpringBoot微信点餐系统(附源码)
点击上方"java进阶架构师",选择右上角"置顶公众号" 20大进阶架构专题每日送达 架构 前后端分离: 补充: setting.xml 文件的作用:setti ...
- 【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo...
开篇先给大家讲段历史故事,博主是湖北襄阳人.襄阳物华天宝,人杰地灵,曾用名襄樊.在2800多年的历史文化中出现了一代名相诸葛亮(卧龙),三国名士庞统(凤雏),魏晋隐士司马徽(水镜先生),唐代大诗人孟浩 ...
- C/C++项目实战:实现在线考试系统,附源码
在线考试系统是国内市场占有率最高的通用化网络考试软件,经过数千家客户的长期实践检验,适用于需要开展网络考试测评的学校.企业.政府和军队,极大地降低客户的硬件投资成本. C++在线考试系统,Access ...
- 【Python爬虫】【2020最新哔哩哔哩验证码识别实战】【滑块验证码】【附源码】
哔哩哔哩实战----验证码识别 利用Selenium来模拟B站登录,并实现线极验验证码的识别. 初步分析 B站的登录界面如下: 登录需要完成:1.账户输入:2.密码输入:3.登录后需要通过滑块来进行验 ...
- Verilog 实现数码管显视驱动【附源码】
目录 1.实验平台 2.实验目的 2.1.实验内容 3.实验流程 3.1.实验原理 3.2.系统架构 3.3.功能模块划分 3.3.1.数据产生模块 模块框图 信号定义 设计文件 3.3.2.数码管驱 ...
最新文章
- 好奇,我们常用的 Integer 内部为什么会去实现 Comparable 接口,他的作用是什么?...
- Android客户端实现七牛云存储文件上传
- Springmvc的服务端数据验证-----Hibernate Validator
- RabbitMQ死信队列代码架构图
- cupsd进程_linux pstree命令显示正在运行的进程的进程树状图
- 聊天软件项目TCP升级版
- linux 配置内网yum源
- Java IO学习笔记(五):内存操作流
- YII学习笔记6.20日
- python13文件_python中13个实用的文件操作
- 走进小作坊(十四)----web2.0策略指南
- Arduino基础入门篇(认识开发板和面包板)
- CentOS的 Oracle 11g R2安装
- 吴恩达机器学习ex2 Logistic Regression (python)
- 分享一些实用的手机应用
- 微信公众号支付 使用基于thinkphp 使用微信官网的sdk
- 秘密是如何被泄露的?自建文件分享神器HFS
- Flutter中如何利用StreamBuilder和BLoC来控制Widget状态
- java查询数据库方法show create
- JS中预处理是一种好无节操的机制
热门文章
- oracle闪回ddl,Oracle闪回详解
- 切边压力机行业调研报告 - 市场现状分析与发展前景预测
- MarkdownPad 2使用教程(语法篇)
- Filter过滤器讲解
- Lession11 集合和泛型(ArrayList方法、Arraylist类、ArrayList添加对象、ArrayList长度、HashTable类、Hashtable类练习-----)
- python图片转文字easyocr_Python OpenCV读取png图像转成jpg图像存储
- win10照片查看器恢复办法
- (个体户)注册公众平台步骤
- 买二手房不后悔先知道8件事 高楼层未必卖得好
- 赵雅智_名片夹(4)_Android中listview可折叠伸缩仿手风琴效果(静态)