首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
资源利用率报告中的LUT和LUTRAM有什么区别
2
xilinx verilog语法技巧(三)--RAM的初始化
3
为什么推荐使用XPM?
4
Block RAM的基本结构
5
为什么要用XPM_MEMORY
6
Vivado中用于时钟操作的几个Tcl命令
7
影响FPGA时序的进位链(Carry Chain), 你用对了么??
8
动态时钟相位
9
动态时钟频率
10
如何缩短Vivado运行时间
11
你的FPGA设计有这些缺陷吗
12
report_utilization远比你想象的强大
13
vivado如何快速找到schematic中的object
14
FPGA中的CLOCK REGION和SLR是什么含义
15
FPGA中的BEL, SITE, TILE是什么含义
16
如何快速查找目标cell
17
都是pin,有什么区别
18
都是net,有什么区别
19
一张图看懂cell, pin, net, port
20
如何快速找到组合逻辑生成的时钟
21
【Vivado那些事】Vivado下头文件使用注意事项
22
Pblock可以这么画
23
关于Pblock的8个必知问题
24
常用的跟Pblock相关的Tcl命令
25
【Vivado】那些事儿-汇总篇
26
【Vivado那些事】Xilinx FPGA普通IO能不能直接接入PLL作为时钟输入
27
Storage Elements
28
D触发器都有哪几种类型?对应什么样的代码?
29
Xilinx 7系列FPGA逻辑单元理解
30
Vivado下查看芯片资源
31
对RAM初始化怎么做
32
干货:Vivado 直接修改RAM初始化文件,避免重新综合、实现的方法
33
借助Elaborated Design优化RTL代码
34
一张图理解什么是好的设计层次
35
9个关于SSI芯片的必知问题
36
Vivado 2018.3 report_qor_suggestions怎么用
37
Vivado 2019.1新特性(5):更新的report_qor_suggestions
38
Xilinx的高质量时钟输出ODDR原语【随路时钟】【全局时钟网络】【ZC706输出时钟】【ZYNQ】
39
FPGA中BEL Site Tile FSR SLR分别指什么?
40
跟I/O相关的几个命令
41
Vivado Non-Project模式
42
FPGA中降低时钟skew的几种方法
43
FPGA时序优化之Reduce MUXF Mapping
44
什么是Forwarded Clock?
45
几个常见问题
46
如何让同一层次的模块在布局时更紧凑一些
47
什么情况下要用OOC综合方式
48
Vivado 2019.1新特性(2):report_ram_utilization
49
URAM和BRAM有什么区别
50
write_first/read_first/no_change什么区别
清单首页FPGA文章详情

影响FPGA时序的进位链(Carry Chain), 你用对了么??

  在FPGA中我们写的最多的逻辑是什么?相信对大部分朋友来说应该都是计数器,从最初板卡的测试时我们会闪烁LED,到复杂的AXI总线中产生地址或者last等信号,都会用到计数器,使用计数器那必然会用到进位链。

  可能很多刚开始接触FPGA的同学没听过进位链,也就是Carry Chain,我们这里再回顾一下。FPGA的三个主要资源为:

  1. 可编程逻辑单元
    • 可配置逻辑单元(Configurable Logic Block, CLB)
    • 存储单元
    • 运算单元(DSP48)
  2. 可编程I/O资源
  3. 布线资源

  其中,CLB在FPGA中最为丰富,在7系列的FPGA中,一个CLB中有两个Slice,Slice中包含4个LUT6、3个数据选择器MUX、两个独立进位链(Carry4,Ultrascale是CARRY8)和8个触发器。

  首先,我们来看下Carry Chain的结构原理,其输入输出接口如下:

image

其中,

  • CI是上一个CARRY4的进位输出,位宽为1;
  • CYINT是进位的初始化值,位宽为1;
  • DI是数据的输入(两个加数的任意一个),位宽为4;
  • SI是两个加数的异或,位宽为4;
  • O是加法结果输出,位宽为4;
  • CO是进位输出,位宽为4;(为什么进位输出是4bit?后面有解释)

Carry4的内部结构如下图所示:

image

  这里我们要先解释一下FPGA中利用Carry Chain实现加法的原理,比如两个加数分别为a = 4'b1000b=4'b1100,其结果应该是8+12=20

代码语言:javascript
复制
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的代码如下:

代码语言:javascript
复制
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

综合之后的电路如下:

image

  在本程序中,加数为din_adin_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的级联,时序情况就好了很多。

对比程序如下:

代码语言:javascript
复制
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上,供大家免费下载。

下一篇
举报
领券