FPGA的时序问题,包括两种时序问题。一种是FPGA内部的寄存器到寄存器之间的时序,另外一种是FPGA接口,外部的寄存器到达FPGA内部的寄存器,也就是接口IO的时序问题。
FPGA的内部寄存器的时序问题,包括建立时间与保持时间。如下图所示。
建立时间里包含两条路径,一个是数据走的路径,一个是时钟走的路径。我们要保证的是,在时钟来临之前,数据已经准备好了一段时间。

数据时间Tdata=Tclk1+Tco+Tdata
时钟时间Tclk=Tclk2+Tcycle-Tsu
Tclk1是时钟源到达第一个寄存器的时间,Tco是时钟沿到数据稳定输出的时间,Tdata是两个寄存器之间的组合逻辑加上布线的延迟, Tclk2是时钟源到达第二个寄存器的时间,Tsu是一个最小的建立时间,是时钟沿到达之前,数据需要保持的最小时间,Tcycle是一个时钟周期。
上面的公式需要满足数据的时间需要小于时钟时间,Tdata<Tclk,即Tclk1+Tco+Tdata<Tclk2+Tcycle-Tsu;那通过这个公式,就可以算出slack是多大
slack=Tclk2+Tcycle-Tsu-Tclk1-Tco-Tdata;
那么能做到的一个最大的频率为Tcycle
举个例子,比如Tcycle=10ns,Tclk2=2ns,Tclk1=3ns, Tsu=0.5ns,Tdata=3ns,Tco=1ns那么
slack=2+10-0.5-3-3-1=4.5ns;
那么最大的就是slack=0的时候Tcycle的值,那么就是5.5ns。就是Fmax=1/(Tcycle-slack)。

按照余量来讲,我用的芯片是xcvu9p-flga2104-3-e,周期可以再设置为10-4.735ns=5.5ns,但实际上设置到4.5ns的时钟周期,还是WNS还是正的。那再设置一下,4.5ns-0.478ns=4ns左右。
![]()

时钟周期设置为4ns,WNS还是为正数,那周期再设置小一点

时钟周期设置为3.6ns,WNS还是为正好为0
再设置小一点,设置为3ns,那么

那下面先去看一下有哪些违例的路径,并且看一下xcvu9p-flga2104-3-e这个芯片的Tco,Tsu的大小等。

如上图所示,违例路径就是从cnt的32位寄存器到dout的32位寄存器之间的路径。我们看一下两个寄存器的组合逻辑有多少级。下面点开一条路径的原理图,可以看到组合逻辑非常长,有14级的组合逻辑,14级正好是勉强可接受,后续优化的方向是把14级组合逻辑拆分为2个7级的组合逻辑。
组合逻辑级数(Levels)的可接受范围,完全取决于时钟频率,核心公式:
单级逻辑延迟 ≈ 0.1ns(xcvu9p-3-e 高速器件)
可接受级数 ≈ 时钟周期 (ns) ÷ 0.1ns ÷ 2(留 50% 裕量)

下面看
里的这几个参数。下面是这是时钟从源端 → 发送寄存器 cnt_reg[3]/C 的延迟,也就是公式里的 Tclk1。


这是数据从发送寄存器 cnt_reg[3]/Q → 接收寄存器 dout_reg[30]/D 的完整延迟,包含 Tco(寄存器输出延迟)和 Tdata(组合逻辑 + 布线延迟)。
1. 读出 Tco(寄存器时钟→输出延迟)
第一行:
FDCE (Prop DFF2 SLICEL C Q) → Tco = 0.072ns
这是发送寄存器 cnt_reg[3] 的时钟沿到数据输出 Q 的延迟,xcvu9p-3-e 的典型值就是 0.07ns 左右,远小于你公式里假设的 1ns!
2. 读出 Tdata(组合逻辑 + 布线延迟)
从第二行开始,到最后一行 FDCE (dout_reg[30]/D) 之前,所有延迟的总和,就是 Tdata。
我们把关键段加起来:
第一行 net 延迟:0.312ns
LUT2 + net:0.047 + 0.010 = 0.057ns
CARRY8 + net:0.133 + 0.048 = 0.181ns
后续所有 CARRY8、LUT、net 延迟累加...
最终数据到达接收寄存器 D 端的总延迟(Arrival Time)= 3.994ns
✅ 结论:
Tco = 0.072ns
Tdata = 3.994ns - 0.500ns (Tclk1) - 0.072ns (Tco) = 3.422ns
(完全对应你之前路径表的 Total Delay=3.494ns,误差来自小数位截断)


这是时钟从源端 → 接收寄存器 dout_reg[30]/C 的延迟,以及接收寄存器的建立时间 Tsu。
1. 读出 Tclk2(接收端时钟延迟)


2. 读出 Tsu(接收寄存器建立时间)
最后一行:
FDCE (Setup HFF2 SLICEM C D) → Tsu = 0.321ns
这是接收寄存器 dout_reg[30] 要求的建立时间,是器件的固有参数,工具从器件库直接读取。
注意:这里包含了时钟不确定性(-0.300ns)的修正,真实 Tsu 约 0.02ns,工具加了安全裕量。
3. 读出 Required Time(要求到达时间)
最终 Required Time = 3.521ns
完全对应公式:
Required Time=T clk2+T cycle−Tsu=0.5+3.0−(−0.321)=3.521ns
(这里的负号是因为 Tsu 是 “提前稳定的时间”,工具计算时做了修正)
时序优化:
优化的原代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2026/04/15 13:29:03
// Design Name:
// Module Name: module_timing
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
/*
module module_timing(
input clk,
input rst_n,
output reg [31:0] dout
);
reg [31:0] cnt;
// 计数器(正常时序)
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 32'd0;
else
cnt <= cnt + 1'b1;
end
// 关键:超长组合逻辑链 → 制造大 Tdata
wire [31:0] comb_delay;
assign comb_delay = (((((cnt * 1234) + 5678) ^ 9876)
* 4321) + 13579) ^ 24680
* cnt[15:0] + cnt[31:16];
// 组合逻辑结果直接打一拍
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
dout <= 32'd0;
else
dout <= comb_delay;
end
endmodule*/
优化后的代码如下:
module module_timing(
input clk,
input rst_n,
output reg [31:0] dout
);
reg [31:0] cnt;
// 计数器不变
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 32'd0;
else
cnt <= cnt + 1'b1;
end
// ===================== 优化1:寄存器复制(解决扇出大 → 布线延迟高)=====================
reg [31:0] cnt_a;
reg [31:0] cnt_b;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_a <= 0;
cnt_b <= 0;
end else begin
cnt_a <= cnt; // 复制一份 cnt
cnt_b <= cnt; // 再复制一份 cnt
end
end
// ===================== 优化2:插入流水线寄存器(砍半逻辑延迟)=====================
reg [31:0] pipe_stage;
// 前半段组合逻辑(只使用 cnt_a,扇出减半)
wire [31:0] comb_part1;
assign comb_part1 = (((cnt_a * 1234) + 5678) ^ 9876);
// 插入一级寄存器 → 最关键!
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
pipe_stage <= 32'd0;
else
pipe_stage <= comb_part1;
end
// 后半段组合逻辑(只使用 cnt_b,扇出减半)
wire [31:0] comb_part2;
assign comb_part2 = (pipe_stage * 4321) + 13579 ^ 24680 * cnt_b[15:0] + cnt_b[31:16];
// 最终输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
dout <= 32'd0;
else
dout <= comb_part2;
end
endmodule
把组合逻辑的长度变短,优化结果如下:

组合逻辑的长度减半

那现在我们去对比代码优前后的时序:
首先Tclk1这里没有变化,两者都是一样的。
其次是逻辑延迟与布线延迟,如下图,可以看到,把14级的组合逻辑拆分为7级的组合逻辑,逻辑延迟反而变大了,布线延迟大幅压缩。综合器为了塞得更紧凑,甚至会把逻辑合并得更密,导致单级更慢、总逻辑延迟更高。这也就是流水线主要优化布线,其次才是逻辑级数。
优化前

优化后:

同时你会发现时钟走线的FDCE这个值从0.321ns变为0.023ns,这不是Tsu变了,Tsu(Setup Time)是FPGA 寄存器的「硬件固有参数」,由器件工艺、速度等级决定,工具在计算Required Time时,叠加了不同的「时序悲观性 / 不确定性」修正,最终呈现的「有效建立时间要求」变了。

1132

被折叠的 条评论
为什么被折叠?



