时序约束实战篇

时序约束实战


本文摘抄自:
个人网站:http://www.technomania.cn/
微信公众号:Quant_times、Reading_Times

行万里路–时序约束实战篇

我们以Vivado自带的wave_gen工程为例,该工程的各个模块的功能较为明确,如下图所示。为了引入异步时钟域,我们在此程序上增加了另一个时钟–clk2,该时钟产生脉冲信号pulse,samp_gen中在pulse为高时才产生信号。
在这里插入图片描述

1. 梳理时钟树

我们首先要做的就是梳理时钟树,就是工程中用到了哪些时钟,各个时钟之间的关系又是什么样的,如果自己都没有把时钟关系理清楚,不指望综合工具会把所有问题暴露出来。
在我们这个工程中,有两个主时钟,四个衍生时钟,如下图所示。
在这里插入图片描述
确定了主时钟和衍生时钟后,再看各个时钟是否有交互,即clka产生的数据是否在clkb的时钟域中被使用。
在这个工程比较简单,只有两组时钟之间有交互,即:
a. clk_rx与clk_tx
b. clk_samp与clk2
其中,clk_rx和clk_tx都是从同一个MMCM输出的,两个频率虽然不同,但他们却是同步的时钟,因此他们都是从同一个时钟分频得到,因此它们之间需要用set_false_path来约束;而clk_samp和clk2是两个异步时钟,需要用asychronous来约束。
在这里插入图片描述
注:图中只有clk_rx到clk_tx的箭头,不应该有从clk_tx到clk_rx的箭头。
完成以上两步,就可以进行具体的时钟约束操作了。

2. 约束主时钟

在这一讲开讲之前,我们先把wave_gen工程中的wave_gen_timing.xdc中的内容删掉,即先看下载没有任何时序约束的情况下会综合处什么结果?
对工程综合并Implementation后,Open Implementation Design,会看到下图所示内容。
在这里插入图片描述
可以看到,时序并未收敛。由于在改工程中,用了一个MMCM,并在里面设置了输入信号频率,因此这个时钟软件会自动加上约束,则不存在之前讲的“如果我们不加时序约束,软件是无法得知我们的时钟周期是多少,PAR后的结果是不会提示时序警告的”。
接下来,我们在Tcl命令行中输入report_clock_networks -name mainclock,显示如下:
在这里插入图片描述
输入下列语句,创建时钟

create_clock -name clk2 -period 25 [get_ports clk_in2]

3. 约束衍生时钟

系统中有4个衍生时钟,但其中有两个是MMCM输出的,不需要我们手动约束,因此我们只需要对clk_samp和spi_clk进行约束即可。约束如下:

create_generated_clock -name clk_samp -source [get_pins clk_gen_i0/clk_core_i0/clk_tx] -divide_by 32 [get_pins clk_gen_i0/BUFHCE_clk_samp_i0/o]
create_generated_clock -name spi_clk -source [get_pins dac_spi_i0/out_ddr_flop_spi_clk_i0/ODDR_inst/C] -divide_by 1 -invert [get_pins spi_clk_pin]

我们再运行report_clocks,显示如下:
在这里插入图片描述
我们再理论篇“create_generated_clock”一节中讲到,我们可以重新设置Vivado自动生成衍生时钟的名字,这样可以更方便我们后续的使用。按照前文所讲,只需设置name和source参数即可,其中这个source可以直接从report_clocks中得到,因此我们的约束如下:

create_generated_clock -name clk_tx -source [get_pins clk_gen_i0/clk_core_i0/inst/mmcm_adv_inst/CLKIN1] [get_pins clk_gen_i0/clk_core_i0/inst/mmcm_adv_inst/CLKOUT1]
create_generated_clock -name clk_rx -source [get_pins clk_gen_i0/clk_core_i0/inst/mmcm_adv_inst/CLKIN1] [get_pins clk_gen_i0/clk_core_i0/inst/mmcm_adv_inst/CLKOUT0]

大家可以对比一下repot_clocks的内容和约束指令,很容易就能看出他们之间的关系。
把上述的约束指令在Tcl中运行后,我们再运行一遍repot_clocks,显示如下:
在这里插入图片描述
在时序的分析中,我们看到,clk_samp和clk2这两个异步时钟之间存在数据交互,因此要进行约束,如下:
在这里插入图片描述

4. 延迟约束

对于延迟约束,比较麻烦一些,因为有时还要计算PCN上的走线延迟导致的时间差。而且不加延迟约束,Vivado也只是在Timing Report中提示warning,并不会导致时序错误。
在这里插入图片描述
比如在很多的ADC设计中,输出的时钟边沿刚好是数据的中心位置,而如果我们不加时序约束,则Vivado会默认时钟和数据是对齐的。
在这里插入图片描述
对于输入管脚,首先判断捕获时钟是主时钟还是衍生时钟,如果是主时钟,直接用set_input_delay即可,如果是衍生时钟,要先创建虚拟时钟,然后再设置delay。对于输出管脚,判断有没有输出随路时钟,若有,则直接使用set_output_delay,若没有,则需要创建虚拟时钟。
在本工程中,输入输出数据管脚的捕获时钟如下图所示:
在这里插入图片描述
根据上表,我们创建的延迟约束如下,其中的具体数字在实际工程中药根据上下游器件的时序关系(在各个器件手册上可以找到)和PCB走线延迟决定。未避免有些约束有歧义,我们把前面所有的约束也加进来。
在这里插入图片描述

5. 伪路径约束

在不加伪路径的时序约束是,Timing Report会提示很多的erro,其中就有跨时钟域的error.
在这里插入图片描述
我们可以直接在上面右键,然后设置来哥哥时钟的伪路径。
在这里插入图片描述
这样会在xdc中自动生成约束。
我们可以手动天界这两个时钟的伪路径如下:
在这里插入图片描述
伪路径的设置是单向的,如果两个时钟直接存在相互的数据的传输,则还需要添加从clk_tx到clk_rx的路径,这个工程中只有从rx到tx的数据传输,因此这一条就可以了。
异步复位也需要添加伪路径,rst_pin的复位输入在本工程中就是当做异步复位使用,因此还需要添加一句:
在这里插入图片描述
对于clk_samp和clk2,它们之间存在数据交换,但我们在前面已经约束过asynchronous了,这里就可以不用重复约束了。
这里需要提示一点,添加上面这些约束后,综合时会提示xdc文件的warning。
在这里插入图片描述
但这可能是Vivado综合过程中,读取到该约束文件时,内部电路并未完全建好,就出现了没有发现clk_gen_i0/clk_core_io/inst/mmcm_adv_inst/CLKIN1等端口的情况,有如下几点证明:

  1. 这个路径在代码中确实存在
  2. 若把该sdc文件,设置为仅在Implementation中使用,则不会提示该warning
  3. 在Implementation完成后,无论是Timing Report还是通过tcl的report_clocks指令,都可以看到这几个时钟已经被正确约束。下图所示即为设置完上面的约束后的Timing Report。
    4.

6. 多周期路径约束

多周期路径,我们一般按照以下几个步骤来约束:
1.带有使能的数据
首先来看带有使能的数据,在本工程中的Timing Report中,也提示了同一个时钟域之间的几个路径建立时间不满足要求
在这里插入图片描述
其实这几个路径都是带有使能的路径,使能的周期为2倍的时钟周期,本来就应该在2个时钟周期内去判断时序收敛。因此,我们添加时序约束:

set_multicycle_path 2 -setup -from [get_cells {cmd_parse_i0/send_resp_data_reg[*]} -include_replicated_objects] -to [get_cells {resp_gen_i0/to_bcd_i0/bcd_out_reg[*]}]
set_multicycle_path 1 -hold -from [get_cells {cmd_parse_i0/send_resp_data_reg[*]} -include_replicated_objects] -to [get_cells {resp_gen_i0/to_bcd_i0/bcd_out_reg[*]}]

也可以这样写

set_multicycle_path -from [get_cells {cmd_parse_i0/send_resp_data_reg[*]} -include_replicated_objects] -to [get_cells {resp_gen_i0/to_bcd_i0/bcd_out_reg[*]}] 2
set_multicycle_path -hold -from [get_cells {cmd_parse_i0/send_resp_data_reg[*]} -include_replicated_objects] -to [get_cells {resp_gen_i0/to_bcd_i0/bcd_out_reg[*]}] 1

我们也可以直接右键通过GUI的方式进行约束,效果是一样的。
在工程的uart_tx_ctl.v和uart_rx_ctl.v文件中,也存在带有使能的数据,但这些路径在未加多路径约束时并未报出时序错误或者警告。
在接收端,捕获时钟频率是200MHz,串口速率是115200,采用16倍的Oversampling,因此使能信号周期是时钟周期的200e6/115200/16=108.5倍。
在接收端,捕获时钟频率是200MHz,串口速率是115200,采用16倍的Oversampling,因此使能信号周期是时钟周期的166.667e6/115200/16=90.4倍。
因此,时序约束如下:

#串口接收端
set_multicycle_path -from [get_cells uart_rx_i0/uart_rx_ctl_i0/* -filter IS_SEQUENTIAL] -to [get_cells uart_rx_i0/uart_rx_ctl_i0/* -filter IS_SEQUENTIAL] 108
set_multicycle_path -hold -from [get_cells uart_rx_i0/uart_rx_ctl_i0/* -filter IS_SEQUENTIAL] -to [get_cells uart_rx_i0/uart_rx_ctl_i0/* -filter IS_SEQUENTIAL] 107
#串口发送端
set_multicycle_path -from [get_cells uart_tx_i0/uart_tx_ctl_i0/* -filter IS_SEQUENTIAL] -to [get_cells uart_tx_i0/uart_tx_ctl_i0/* -filter IS_SEQUENTIAL] 90
set_multicycle_path -hold -from [get_cells uart_tx_i0/uart_tx_ctl_i0/* -filter IS_SEQUENTIAL] -to [get_cells uart_tx_i0/uart_tx_ctl_i0/* -filter IS_SEQUENTIAL] 89
  1. 两个有数据交互的时钟之间存在相位差 在本工程中,没有这种应用场景,incident不需要添加此类约束
  2. 存在快时钟到慢时钟的路径 在本工程中,没有这种应用场景,incident不需要添加此类约束
  3. 存在慢时钟到快时钟的路径 在本工程中,没有这种应用场景,incident不需要添加此类约束

至此,重新Synthesis和Implementation后,可以看到,已经没有时序错误
在这里插入图片描述
到这里,教科书版的时序约束教程就基本结束了。但在我们平时的工程中,跟上面这种约束还是有差异的:

  1. 首先是虚拟时钟,这个约束在平时的工程中基本不会用到,像需要设计虚拟时钟的场景,我们也都是通过设计来保证时序收敛,设置虚拟时钟的意义不大。
  2. 第二就是output delay,在FPGA的最后一级寄存器到输出的路径上,往往都使用了IOB,也就是IO block,因此最后一级寄存器的位置是固定的,从buffer到pad的走线延时是确定的。在这种情况下,是否满足时序要求完全取决于设计,做约束只是验证一下看看时序是否收敛。所以也基本不做。但是input delay是需要的,因为这是上一级器件输出的时序关系。
  3. 第三个就是多周期路径,我们讲了那么多多周期路径的应用场景,但实际我们是根据Timing Report来进行约束的,即便那几种场景都存在,但如果Timing Report中没有提示任何时序warning,我们往往也不胡去添加约束。
  4. 第四个就是在设置了多周期后,如果还是提示Intra-Clocks Paths的setup time不过,那就要看下程序,是否写的不规范。比如
    在这里插入图片描述
    这么写的话,如果时钟频率稍微高一些,比如250MHz,就很容易导致从regB到regD的setup time不满足要求。因为begin end里面的代码都是按顺序执行的,要在4ns内完成这些复制与判断逻辑,挑战还是挺大的。因此,我们可以这样改写:
    在这里插入图片描述
    把寄存器的赋值分开,功能还是一样的,只是分到了几个always中,这样就不会导致问题了。
<think>好的,我现在需要帮助用户了解SPI时序约束的设计要求。首先,用户的问题涉及SPI(Serial Peripheral Interface)的时序约束,这可能是在FPGA或ASIC设计中常见的需求。我需要结合用户提供的引用资料,尤其是引用[2]和引用[4]中提到的输入输出延时约束,以及引用[1]中的时钟不确定性相关内容。 首先,我应该回顾SPI的基本工作原理。SPI通常包括四个信号:SCLK(时钟)、MOSI(主设备输出从设备输入)、MISO(主设备输入从设备输出)、SS(片选)。时序约束的关键在于确保数据在时钟边沿正确采样,满足建立时间(Tsu)和保持时间(Th)的要求。 接下来,用户提供的引用[2]详细介绍了输入输出延时的计算方法,这可能适用于SPI的主从设备之间的接口约束。例如,主设备输出的数据需要满足从设备的建立和保持时间,这涉及到计算最大和最小输出延时。同样,从设备返回的数据(MISO)需要作为主设备的输入,也需要相应的输入延时约束。 引用[4]提到了虚拟时钟的使用,这在SPI中可能有用,特别是当主设备和从设备的时钟不同源时。例如,如果从设备的时钟是虚拟的,可以通过创建虚拟时钟来设定输入延时,确保数据在正确的时刻被捕获。 另外,引用[1]中的时钟不确定性(set_clock_uncertainty)用于额外增加时序余量,这可能用于处理SPI接口中的时钟抖动或偏差,确保在最坏情况下时序仍能满足。 接下来,我需要将这些概念结合起来,解释如何具体应用到SPI的时序约束中。例如,主设备作为FPGA时,需要约束MOSI和MISO的输入输出延时,考虑外部器件的Tco、PCB走线延迟以及时钟网络延时。同时,可能需要设置虚拟时钟来模拟从设备的时钟,并应用输入输出延时约束。 还需要注意SPI的工作模式,即时钟极性和相位(CPOL和CPHA),这会影响到数据采样的边沿。不同的模式可能导致建立和保持时间的要求不同,因此时序约束需要根据具体模式调整。 此外,时序约束中可能需要考虑跨时钟域的情况,特别是当SPI主从设备使用不同时钟域时,需要适当的同步机制,但这可能超出了基本的时序约束范畴,属于设计架构的问题。 最后,用户可能还关心如何验证这些约束,例如通过时序分析工具检查建立和保持时间是否满足,以及如何调试时序违规。因此,在回答中应该涵盖约束设置的具体命令(如set_input_delay、set_output_delay)、时钟定义,以及可能的时钟不确定性设置,并结合实例说明。 现在需要将这些思路整理成一个结构化的回答,分点说明SPI时序约束的设计要求,包括主设备输出约束、主设备输入约束、时钟不确定性、虚拟时钟的使用等,并结合引用中的相关信息进行支持。同时,生成相关问题,帮助用户进一步学习。</think>### SPI时序约束设计要点 SPI接口的时序约束设计需确保主从设备间数据传输满足建立时间(Tsu)和保持时间(Th)要求,具体分为以下部分: #### 1. **主设备输出约束(MOSI信号)** 主设备向从设备发送数据时,需约束输出延时,确保数据在从设备时钟边沿稳定: - **最大输出延时**:考虑从设备的Tsu、PCB最大延迟和主设备时钟网络最大延时 公式:$T_{output\_max} = T_{su\_slave} + T_{pcb\_max} + T_{clk\_network\_max}$ - **最小输出延时**:确保数据在从设备Th时间前保持稳定 公式:$T_{output\_min} = T_{h\_slave} - T_{pcb\_min} + T_{clk\_network\_max}$ 约束示例(FPGA设计): ```tcl set_output_delay -clock [get_clocks SCLK] -max 3 [get_ports MOSI] set_output_delay -clock [get_clocks SCLK] -min 1 [get_ports MOSI] ``` #### 2. **主设备输入约束(MISO信号)** 主设备接收从设备数据时,需约束输入延时,满足主设备内部寄存器时序要求: - **最大输入延时**:包含从设备Tco、PCB最大延迟和主设备时钟网络最大延时 公式:$T_{input\_max} = T_{co\_slave} + T_{pcb\_max} + T_{clk\_network\_max}$ - **最小输入延时**:确保数据在主设备Th前保持稳定 公式:$T_{input\_min} = T_{co\_slave\_min} + T_{pcb\_min} + T_{clk\_network\_min}$ 约束示例: ```tcl set_input_delay -clock [get_clocks SCLK] -max 4 [get_ports MISO] set_input_delay -clock [get_clocks SCLK] -min 0.5 [get_ports MISO] ``` #### 3. **时钟不确定性设置** 通过`set_clock_uncertainty`增加额外时序余量,覆盖时钟抖动和偏差[^1]: ```tcl set_clock_uncertainty 0.2 [get_clocks SCLK] ``` #### 4. **虚拟时钟应用(异步时钟域)** 当主从设备时钟不同源时,需创建虚拟时钟模拟从设备时钟[^4]: ```tcl create_clock -name virt_clk -period 20 # 虚拟时钟周期需与从设备实际时钟匹配 set_input_delay -clock virt_clk 2 [get_ports MISO] ``` #### 5. **SPI模式影响** 根据CPOL和CPHA参数调整约束: - **CPOL=0**:SCLK空闲低电平;**CPOL=1**:空闲高电平 - **CPHA=0**:数据在第一个边沿采样;**CPHA=1**:在第二个边沿采样 需根据模式调整`-clock_latency`或约束边沿关系。 --- ### 示例约束(主设备为FPGA) ```tcl # 定义主时钟 create_clock -name SCLK -period 10 [get_ports SCLK] # MOSI输出约束(主→从) set_output_delay -clock SCLK -max 3.5 [get_ports MOSI] set_output_delay -clock SCLK -min 1.2 [get_ports MOSI] # MISO输入约束(从→主) set_input_delay -clock SCLK -max 4.1 [get_ports MISO] set_input_delay -clock SCLK -min 0.8 [get_ports MISO] # 设置时钟不确定性 set_clock_uncertainty 0.25 [get_clocks SCLK] ``` ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值