问题阐述
源自通信算法的一个工程,存在两种频率,基带算法频率和中频频率,记为clk_1和clk_2,分别为2kHz与100kHz,对于FPGA为低频时钟。以接收端为例,接收端接收到频率为clk_1的中频信号后需经过变频等操作,变换到基带的clk_2,因此涉及到多bit信号快时钟域到慢时钟域的处理问题,相反的发射端是慢时钟域到快时钟域的问题。这里以接收端为例说明时序约束的方法。
1. 时钟分析
使用的FPGA为ZYNQ7020,时钟为50MHz,由PS端产生,如图所示。
由于clk_1和clk_2频率过低,MMCM和PLL无法生成,因此采用计数器来实现时钟分频。分频代码:
module clk_gen(
input sys_clk, //系统时钟
input sys_rst_n,
output reg clk_1, //基带时钟 2k
output reg clk_2 //中频时钟 100k
);
reg [5:0] clk_cnt_1;
reg [9:0] clk_cnt_2;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n==1'b0)begin
clk_cnt_1<=5'b0;
clk_cnt_2<=10'b0;
clk_1<=1'b0;
clk_2<=1'b0;
end
else if(clk_cnt_1<6'd49)begin
if(clk_cnt_2<10'd249)begin
clk_cnt_2<=clk_cnt_2+1'b1;
end
else begin
clk_cnt_2<=10'b0;
clk_2<=~clk_2;
clk_cnt_1<=clk_cnt_1+1'b1;
end
end
else begin
if(clk_cnt_2<10'd249)begin
clk_cnt_2<=clk_cnt_2+1'b1;
end
else begin
clk_cnt_1<=5'b0;
clk_cnt_2<=10'b0;
clk_1<=~clk_1;
clk_2<=~clk_2;
end
end
仿真:
2. 添加时钟约束
对整个工程综合、实现,看到timing如下:
此时时序没有报错,但实际是因为没有做时序约束引起的,在tcl运行命令:
report_clock_networks -name timing
打开"clock networks":
这里两个未约束时钟就是clk_1,clk_2:
右键时钟,选择"create generated clock"
设置名字,来源时钟,分频系数:
此时再查看"clock networks",时钟都被约束:
输入命令:
report_clocks
可以查看:
generated clocks:
此时再查看xdc文件,多了这两条:
create_generated_clock -name clk_2 -source [get_pins system_i/top_module_0/sys_clk] -divide_by 500 -add -master_clock clk_fpga_0 [get_pins system_i/top_module_0/inst/u_receiver/u_clk_gen/clk_2_reg/Q]
create_generated_clock -name clk_1 -source [get_pins system_i/top_module_0/sys_clk] -divide_by 25000 -add -master_clock clk_fpga_0 [get_pins system_i/top_module_0/inst/u_receiver/u_clk_gen/clk_1_reg/Q]
3. 时序调试
再进行综合,实现,时序出现错误:
查看报告主要是clk_2到clk_1出现了建立时间的错误:
这属于多bit信号跨时钟域处理问题,常见的方法异步FIFO、DMUX等,网上资料很多,但我们这个时钟频率很低,不需要用到这么复杂的方法。
查看"clocking summary":
“waveform"里有时钟的相位信息,clk_1和clk_2是相位偏移为0的时钟,clk_1时钟域对clk_2时钟域采样的时候,二者的时钟沿差不多是重合的,猜测可能在数据变化的时候进行了采样,从而可能会产生亚稳态。但是如果让clk_1和clk_2的相位错开,在clk_2高电平或低电平的中间对数据进行采样,因为clk_2的时钟频率对FPGA来说是很低的,在数据稳定时进行采样可能可以避免亚稳态的出现。
所以我们做一个小的修改,删除xdc里的对clk_2时钟约束,然后对其重新设置:
其他设置都相同,不同的是在"derive from source clock waveform"里选择"by clock edges”,设置的是前三个上升沿或下降沿。
再重新进行综合,xdc文件里也多了代码:
create_generated_clock -name clk_2 -source [get_pins system_i/top_module_0/sys_clk] -edges {1 2 3} -edge_shift {2500.000 7500.000 12500.000} -add -master_clock clk_fpga_0 [get_pins system_i/top_module_0/inst/u_receiver/u_clk_gen/clk_2_reg/Q]
此时的"waveform":
此时时序也不再报错:
后续再修改一下clk_gen的代码,来修改下clk_2的初始相位应该就可以了。