影响FPGA时序的进位链(Carry Chain), 你用对了么??
在FPGA中我们写的最多的逻辑是什么?相信对大部分朋友来说应该都是计数器,从最初板卡的测试时我们会闪烁LED,到复杂的AXI总线中产生地址或者last等信号,都会用到计数器,使用计数器那必然会用到进位链。
可能很多刚开始接触FPGA的同学没听过进位链,也就是Carry Chain,我们这里再回顾一下。FPGA的三个主要资源为:
可编程逻辑单元
可配置逻辑单元(Configurable Logic Block, CLB)
存储单元
运算单元(DSP48)
可编程I/O资源
布线资源
其中,CLB在FPGA中最为丰富,在7系列的FPGA中,一个CLB中有两个Slice,Slice中包含4个LUT6、3个数据选择器MUX、两个独立进位链(Carry4,Ultrascale是CARRY8)和8个触发器。
首先,我们来看下Carry Chain的结构原理,其输入输出接口如下:
其中,
CI是上一个CARRY4的进位输出,位宽为1;
CYINT是进位的初始化值,位宽为1;
DI是数据的输入(两个加数的任意一个),位宽为4;
SI是两个加数的异或,位宽为4;
O是加法结果输出,位宽为4;
CO是进位输出,位宽为4;(为什么进位输出是4bit?后面有解释)
Carry4的内部结构如下图所示:
这里我们要先解释一下FPGA中利用Carry Chain实现加法的原理,比如两个加数分别为a = 4'b1000
和b=4'b1100
,其结果应该是8+12=20
。
a = 4'b1000;
b = 4'b1100;
S = a ^ b = 4'b0100;
D = b = 4'b1100; //D取a也可以
CIN = 0; //没有上一级的进位输入
CYINIT = 0; //初始值为0
// 下面为CARRY4的计算过程,具体的算法跟上图中过程一样
S0 = 0; //S的第0位
O0 = S0 ^ 0 = 0 ^ 0 = 0;
CO0 = DI0 = 0; //上图中的MUXCY,S0为0时,选择1,也就是DI0,S0为1是选择2
S1 = 0;
O1 = S1 ^ CO0 = 0 ^ 0 = 0;
CO1 = DI1 = 0;
S2 = 1;
O2 = S2 ^ CO1 = 0 ^ 1 = 1;
CO2 = CO1 = 0;
S3 = 0;
O3 = S3 ^ CO2 = 0 ^ 0 = 0;
CO3 = DI3 = 1;
加法最终的输出结果为:{CO3,O3,O2,O1,O0} = 5'b10100 = 20。进位输出在CARRY4的内部也使用到了,因此有4个bit的进位输出CO,但输出给下一级的只是CO[3]。
再来看完下面的例子就更清晰了。Example的代码如下:
module top(
input clk,
input [7:0] din_a,
input [7:0] din_b,
output reg[7:0] dout
);
always @ ( posedge clk )
begin
dout <= din_a + din_b;
end
endmodule
综合之后的电路如下:
在本程序中,加数为din_a
和din_b
,图中
1表示CARRY4的进位输出到下一级的进入输入;
2表示输入的一个加数din_a(换成din_b也是可以的);
3表示第二级输入的DI端口,因为第二级CARRY是通过第一级的进位输出进行累加,因此该接口为0;
4表示输入两个加数的异或结果。
可以看出,当进行两个8bit的数据进行加法操作时,会使用两个CARRY4级联,那如果是对48位的数据进行相加,那就会用到12个的CARRY4的级联,这样明显就会使逻辑延迟过大,很容易造成时序不收敛。(这里需要注意的是,在Vivado的默认设置下,如果进行的是12bit以下的数据加1'b1的操作,那么Vivado综合的结果并不会使用CARYY4,而是使用LUT来实现加法器)。
那如何解决这种问题呢?我们可以把加法操作进行拆解,比如拆解成3个16bit的计数器,那这样就会只有4个CARRY4的级联,时序情况就好了很多。
对比程序如下:
module top(
input clk,
input [47:0] din1,
input [47:0] din2,
output reg[47:0] dout1,
output [47:0] dout2
);
always @ ( posedge clk )
begin
dout1 <= din1 + 1'b1;
end
genvar i;
generate
for(i = 0;i < 3;i=i+1) begin:LOOP
wire carry_co;
reg [15:0] carry_o=0;
wire ci;
if(i==0) begin
always @ ( posedge clk )
begin
carry_o <= din2[i*16+:16] + 1'b1;
end
end //if
else begin
always @ (posedge clk) begin
if(LOOP[i-1].carry_co == 1)
carry_o <= carry_o + 1'b1;
end
end //else
assign LOOP[i].carry_co = (LOOP[i].carry_o==16'hffff)?1'b1:1'b0;
assign dout2[i*16+:16] = LOOP[i].carry_o;
end //for
endgenerate
endmodule
打开综合后的schematic后可以发现,在dout2的输出中,每4个CARRY4后都会有一级的触发器,这样时序就会好很多,但这样做的代价是LUT会增加。
对于不同的位宽的数据,我们后面会给出一个通用的Verilog代码,朋友们可以关注github的Rex1168账户,过几天我们会把程序放到GitHub上,供大家免费下载。
想加FPGA技术讨论群的朋友可以加我的微信:xhclshs2。
本文分享自微信公众号 - 科学计算Tech(Quant_Times)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Julia/Python/Matlab基本语法比较
相信很多朋友刚开始做算法时应该都是用matlab做理论模型的验证,后来Python又大火,很多小伙伴又争相学起来python,可过了没多久,一个更牛逼的语言又进入了我们的视野--Julia,号称是有matlab似的直观数学表达式,有C的运算速度。相信又有不少朋友蠢蠢欲动了,而小编发现在刚开始学习某种语言时或者在多个语言之间来回切换时,很容易把它的语法跟其他语言搞混,所以今天我们就整理了一份Julia/Python/Matlab三种算法工程师常用的编程语言的基本语法的比较,小伙伴们可以收藏起来,在忘记某个语法时拿出来看看。 基本数据类型 Python:数字、字符串、列表、元组、集合、字典 Matlab:数字、字符串、逻辑值、表、结构体、元胞数组、函数句柄 Julia:数字、字符串、自定义类型(struct/Union/Tuple/Array等) 注:julia 网上很多教程都是针对0.3版本,跟最新的1.0版有很多语法不太一样,在学习时最好看julia官方文档 Python Matlab Julia 基本操作 类型 动态语言,在运行期间才去做数据类型检查,因此无需指定数据类,在第一次赋值...
- 下一篇
【高并发】如何实现亿级流量下的分布式限流?这些算法你必须掌握!!
点击上方蓝色“冰河技术”,关注并选择“设为星标” 持之以恒,贵在坚持,每天进步一点点! 作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址: https://github.com/sunshinelyz/mykit-delay PS: 欢迎各位Star源码,也可以pr你牛逼哄哄的代码。 写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一、京东618、秒杀、抢购促销等,这些都是典型的大流量高并发场景。关于秒杀,小伙伴们可以参见我的另一篇文章《【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!》 关于【冰河技术】微信公众号,解锁更多【高并发】专题文章。 注意:由于原文篇幅比较长,所以被拆分为:理论、算法、实战(HTTP接口实战+分布式限流实战)三大部分。理论篇参见《【高并发】如何实现亿级流量下的分布式限流?这些理论你必须掌握!!》 计数器 计数...
相关文章
文章评论
共有0条评论来说两句吧...