首页 > 其他分享 >FPGA时序约束实战

FPGA时序约束实战

时间:2024-03-28 11:15:25浏览次数:35  
标签:实战 pin FPGA get uart i0 cells clk 时序

改编自8 FPGA时序约束实战篇之主时钟约束_check timing no clock

 

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

下面我们来一步一步进行时序约束。

梳理时钟树

我们首先要做的就是梳理时钟树,就是工程中用到了哪些时钟,各个时钟之间的关系又是什么样的,如果自己都没有把时钟关系理清楚,不要指望综合工具会把所有问题暴露出来。

在我们这个工程中,有两个主时钟,四个衍生时钟,如下图所示。

确定了主时钟和衍生时钟后,再看各个时钟是否有交互,即clka产生的数据是否在clkb的时钟域中被使用。

这个工程比较简单,只有两组时钟之间有交互,即:

  • clk_rx与clk_tx
  • clk_samp与clk2

其中,clk_rx和clk_tx都是从同一个MMCM输出的,两个频率虽然不同,但他们却是同步的时钟,因此他们都是从同一个时钟分频得到(可以在Clock Wizard的Port Renaming中看到VCO Freq的大小),因此它们之间需要用set_false_path来约束;而clk_samp和clk2是两个异步时钟,需要用asynchronous来约束。

完成以上两步,就可以进行具体的时钟约束操作了。

约束时钟

我们先把wave_gen工程的wave_gen_timing.xdc中的内容都删掉,即先看下在没有任何时序约束的情况下会综合出什么结果?

对工程综合并Implementation后,Open Implemented Design,会看到下图所示内容。

可以看到,时序并未收敛。可能到这里有的同学就会有疑问,我们都已经把时序约束的内容都删了,按我们第一讲中提到的“因此如果我们不加时序约束,软件是无法得知我们的时钟周期是多少,PAR后的结果是不会提示时序警告的”,这是因为在该工程中,用了一个MMCM,并在里面设置了输入信号频率,因此这个时钟软件会自动加上约束。

展开check timing工具

可以看到警告信息

添加以下约束,可以看到报错信息已经变更

create_clock -period 6.000 -name virtual_clock 
#指定 virtual_clock 时钟信号,周期为 6.000 ns。用于同步其他逻辑元件。

set_input_delay -clock [get_clocks -of_objects [get_ports clk_pin_p]] 0.000 [get_ports rxd_pin] 
#设置输入延迟。当接收到 rxd_pin 的信号时,应该考虑时钟信号 clk_pin_p 的 0.000 单位延迟。

set_input_delay -clock [get_clocks -of_objects [get_ports clk_pin_p]] -min -0.500 [get_ports rxd_pin] 
#设置最小延迟。 rxd_pin 的信号必须至少在 clk_pin_p 之前 0.500 单位到达。

set_input_delay -clock virtual_clock -max 0.0 [get_ports lb_sel_pin] 
#设置 lb_sel_pin 的最大延迟,相对于 virtual_clock。最大延迟为 0.0,意味着信号应该立即到达。

set_input_delay -clock virtual_clock -min -0.5 [get_ports lb_sel_pin] 
#设置最小延迟。这意味着 lb_sel_pin 的信号必须至少在 virtual_clock 之前 0.5 单位到达。

set_false_path -from [get_ports rst_pin] 
#指定了一个“假路径”。不需要考虑 rst_pin 信号的时序路径,它不会影响设计的正确性。

 

继续添加约束来解决outputdelay问题

set_output_delay -clock virtual_clock -max 0.0 [get_ports {txd_pin led_pins[*]}] 
#设置输出延迟。当发送到 txd_pin 和 led_pins 的信号时,应该立即发送,不需要额外的延迟。

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_ports spi_clk_pin]
#定义了 spi_clk 生成时钟,来源是 dac_spi_i0/out_ddr_flop_spi_clk_i0/ODDR_inst/C,并且被除以1(即不分频)。用于同步其他逻辑元件。

set_output_delay -clock spi_clk -max 1.000 [get_ports {spi_mosi_pin dac_cs_n_pin dac_clr_n_pin}]
#设置了输出延迟。当发送到 spi_mosi_pin、dac_cs_n_pin 和 dac_clr_n_pin 的信号时,应该在 spi_clk 之前最多延迟 1.000 单位。

set_output_delay -clock spi_clk -min -1.000 [get_ports {spi_mosi_pin dac_cs_n_pin dac_clr_n_pin}]
#设置了最小延迟。信号应该至少在 spi_clk 之前 1.000 单位到达。

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
#这行代码设置了多周期路径。它指定了从 cmd_parse_i0/send_resp_data_reg[*] 到 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

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

# For 193.75 MHz CLOCK_RATE_TX
#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}] 105
#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}] -hold 104

# For 166.667 MHz CLOCK_RATE_TX
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

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]

# To keep the synchronizer registers near each other
set_max_delay -from [get_cells clkx_nsamp_i0/meta_harden_bus_new_i0/signal_meta_reg] -to [get_cells clkx_nsamp_i0/meta_harden_bus_new_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells clkx_pre_i0/meta_harden_bus_new_i0/signal_meta_reg] -to [get_cells clkx_pre_i0/meta_harden_bus_new_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells clkx_spd_i0/meta_harden_bus_new_i0/signal_meta_reg] -to [get_cells clkx_spd_i0/meta_harden_bus_new_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells lb_ctl_i0/debouncer_i0/meta_harden_signal_in_i0/signal_meta_reg] -to [get_cells lb_ctl_i0/debouncer_i0/meta_harden_signal_in_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells samp_gen_i0/meta_harden_samp_gen_go_i0/signal_meta_reg] -to [get_cells samp_gen_i0/meta_harden_samp_gen_go_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells uart_rx_i0/meta_harden_rxd_i0/signal_meta_reg] -to [get_cells uart_rx_i0/meta_harden_rxd_i0/signal_dst_reg] 2.000
set_max_delay -from [get_cells rst_gen_i0/reset_bridge_clk_rx_i0/rst_meta_reg] -to [get_cells rst_gen_i0/reset_bridge_clk_rx_i0/rst_dst_reg] 2.000
set_max_delay -from [get_cells rst_gen_i0/reset_bridge_clk_tx_i0/rst_meta_reg] -to [get_cells rst_gen_i0/reset_bridge_clk_tx_i0/rst_dst_reg] 2.000
set_max_delay -from [get_cells rst_gen_i0/reset_bridge_clk_samp_i0/rst_meta_reg] -to [get_cells rst_gen_i0/reset_bridge_clk_samp_i0/rst_dst_reg] 2.000

此外,由于对工程加入了修改,还有一个clk_in2被遗忘了,接下来,我们在tcl命令行中输入report_clock_networks -name main,显示如下:

可以看出,Vivado会自动设别出两个主时钟,其中clk_pin_p是200MHz,这个是直接输入到了MMCM中,因此会自动约束;另一个输入时钟clk_in2没有约束,需要我们手动进行约束。

或者可以使用check_timing -override_defaults no_clock指令,这个指令我们之前的内容讲过,这里不再重复讲了。

在tcl中输入create_clock -name clk2 -period 25 [get_ports clk_in2]

注:在Vivado中,可以直接通过tcl直接运行时序约束脚本,运行后Vivado会自动把这些约束加入到xdc文件中。

再执行report_clock_networks -name main,显示如下:

此时重新运行检测,时序约束已经被满足

 

标签:实战,pin,FPGA,get,uart,i0,cells,clk,时序
From: https://www.cnblogs.com/radiumlrb/p/18101116

相关文章

  • 实测52.4MB/s!全国产ARM+FPGA的CSI通信案例分享!
    CSI总线介绍与优势CSI(CMOSsensorparallelinterfaces)总线是一种用于连接图像传感器和处理器的并行通信接口,应用于工业自动化、能源电力、智慧医疗等领域,CSI总线接口示意图如下所示(以全志科技T3处理器的CSI0为例)。  图1高带宽:CSI总线支持高速数据传输,可以满足多通道高速......
  • 时序信号高低频分析——EMD和EEMD对比
    时序信号高低频分析——EMD和EEMD对比介绍时序信号高低频分析是信号处理领域中的一个重要任务,用于分解信号中的高频和低频成分,从而更好地理解信号的特性和行为。在高低频分析中,经验模态分解(EmpiricalModeDecomposition,EMD)和改进的经验模态分解(EnhancedEmpiricalModeD......
  • octave实战笔录(1)
    基础矩阵与基本运算>>exp(2)ans=7.3891>>exp(pi)ans=23.141>>exp(i*pi)ans=-1.0000e+00+1.2246e-16i>>a=[1,2,3]a=123>>b=[[1,2,3],[4,5,6]]b=123456>>b=[[1,2,3];[4,5,6]]b=......
  • FPGA原语
    ODDR代表的是双数据速率输出寄存器(OutputDoubleDataRateRegister)。这种原语用于在一个时钟周期内产生两个数据输出,通常用于高速数据传输和时钟数据恢复等应用。在以下示例VHDL代码中,ODDR原语被用来生成一个双数据速率的输出信号ad9653_1clk。ad1_clk:ODDRgenericmap(......
  • Xilinx ZYNQ 7000+Vivado2015.2系列(八)ARM+FPGA的优势,PS控制PL产生需要的PWM波(基于AXI
    上一节我们观察了AXI总线的信号,了解了基于AXI总线读写的时序,这一节我们继续探索基于AXI总线的设计,来看一看ZYNQ系列开发板的独特优势,PS可以控制PL产生定制化的行为,而不需要去动硬件代码。这次实验是产生频率和占空比可调的PWM(PulseWidthModulation)信号,调用8次,产生8路PWM......
  • Xilinx ZYNQ 7000+Vivado2015.2系列(七)软硬件联合Debug观察AXI总线读、写时各信号的时
    前面一节我们学会了创建基于AXI总线的IP,但是对于AXI协议各信号的时序还不太了解。这个实验就是通过SDK和Vivado联合调试观察AXI总线的信号。由于我们创建的接口是基于AXI_Lite协议的,所以我们实际观察到是AXI_Lite协议的信号时序。具体做法是创建一个基于AXI总线的加法器模块,在......
  • 901-深入浅出Python量化交易实战的配套视频和代码(段小手)中文PDF+源代码(源文件)
    小瓦的故事——从零开始本书源于一个真实的故事,故事的主角是一位名叫小瓦的姑娘。小瓦出生在一个普通的家庭,父母都是老实淳朴的普通人,靠着并不丰厚的收入把小瓦养育成人。18岁那年,小瓦考上了一所不好不坏的大学,所学专业是一个就业前景算不上理想的专业。再加上她本身也谈不......
  • RestCloud数据集成平台-监听SqlServer数据库表,并同步数据到MongoDB数据库表详细教程(实
    上一篇:RestCloud数据集成平台-Windows全量包安装部署详细教程1.数据源管理数据源主要用来建立与用户的数据库的链接。数据源管理主要用来对用户添加的所有数据链接进行管理,主要包括新建数据源、测试链接、修改链接、复制链接、查询链接和删除链接等功能。1.1.创建链接......
  • 人工智能时代,前端全栈成就独立开发工程师 next.js 开发实战
    由于next.js是基础于react所以在正式学习next.js之前我们了解一下react一模块,就是一个文件,向外提供一些功能的文件,之所要要折分模块就是因为功能越来越复杂,为了方便管化或管理。第一部份,我们用最原始的,没有用脚手架,所以要手工加载三个文件,一个dom 一个react 一个babel......
  • 鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
    ......