在Vivado中进行HDL代码设计,不仅需要描述数字逻辑电路中的常用功能,还要考虑如何发挥Xilinx器件的架构优势。目前常用的HDL语言有三种。VHDL语言的优势有:

  • 语法规则更加严格;
  • 在HDL源代码中初始化RAM组件更容易;
  • 支持package;
  • 自定义类型;
  • 枚举类型;
  • 没有reg和wire之间的混淆。

Verilog语言的优势有:

  • 与C语言类似的语法;
  • 代码结构更紧凑;
  • 支持块注释(老版VHDL不支持);
  • 没有像VHDL一样的重组件实例化。

SystemVerilog语言的优势有:

  • 与Verilog相比代码结构更加紧凑;
  • 结构体和枚举类型有更好的扩展性;
  • 更高抽象级别的接口;
  • Vivado综合支持SystemVerilog 2012。

以博主接触的情况看,目前使用最广泛的应该是Verilog语言,替代VHDL成为国内大学教学的主流。SystemVerilog其实有更高级别的描述能力,无论是设计还是仿真性能也更强大,目前很多国外大学都使用SystemVerilog作为教学语言。本文以Verilog语言为基础讲述HDL代码编写技巧。RAM部分内容比较多,单独放在第27篇讲述。


1.触发器、寄存器和锁存器

Vivado综合可以识别出带有如下控制信号的触发器(Flip-Flop)和寄存器(register):上升沿或下降沿时钟、异步置位或复位信号、同步置位或复位信号、时钟使能信号。Verilog中对应着always块,其敏感列表中应该包含时钟信号和所有异步控制信号。

使用HDL代码设计触发器、寄存器时注意如下基本规则:

  • 寄存器不要异步置位/复位,否则在FPGA内找不到对应的资源来实现此功能,会被优化为其它方式实现。
  • 触发器不要同时置位和复位。Xilinx的触发器原语不会同时带有置位和复位信号,这样做会对面积和性能产生不利影响。
  • 尽量避免使用置位/复位逻辑。可以采用其它方法以更少的代价达到同样 的效果,比如利用电路全局复位来定义初始化内容。
  • 触发器控制信号的输入应总是高电平有效。如果设置为低电平有效,会插入一个反相器,对电路性能会产生不利影响。

Vivado综合工具根据HDL代码会选择4种寄存器原语:

  • FDCE:带有时钟使能和异步清0的D触发器;
  • FDPE:带有时钟使能和异步预置(Preset)的D触发器;
  • FDSE:带有时钟使能和同步置位的D触发器;
  • FDRE:带有时钟使能和同步复位的D触发器;

寄存器的内容会在电路上电时初始化,因此在申明信号时最好设定一个默认值。综合后,报告中可以看到寄存器的使用情况。下面给出一个寄存器的代码实例:

//上升沿时钟、高电平有效同步清0、高电平有效时钟使能8Bits寄存器
module registers_1(input [7:0] d_in,input ce, clk, clr,output [7:0] dout
);reg [7:0] d_reg;
always @ (posedge clk)if(clr) d_reg <= 8'b0;else if(ce) d_reg <= d_in;assign dout = d_reg;
endmodule

Vivado综合还会报告检测出的锁存器(Latches),通常这些锁存器是由HDL代码设计错误引起的,比如if或case状态不完整。综合会为检测出的锁存器报告一个WARNING(Synth 8-327)。下面给出一个锁存器的代码示例:

//带有Postive Gate和异步复位的锁存器
module latches (input G,input D,input CLR,output reg Q
);always @ *if(CLR) Q = 0;else if(G) Q = D;endmodule

2.三态缓冲器

三态缓冲器(Tristate buffer)通常由一个信号或一个if-else结构来建模,缓冲器可以用来驱动内部总线,也可以驱动外部板子上的总线。如果使用if-else结构,其中一个分支需要给信号赋值为高阻状态。

当三态缓冲器通过管脚驱动外部总线时,使用OBUFT原语实现;当驱动内部总线时,使用BUFT原语,综合工具会将其转换为用LUT实现的逻辑电路。下面给出处理三态缓冲器的代码示例:

//使用always块描述三态
module tristates_1 (input T, I, output reg O
);always @(T or I)if (~T) O = I;else O = 1'bZ;endmodule//使用并行赋值描述三态
module tristates_2 (input T, I, output O);assign O = (~T) ? I: 1'bZ;endmodule

3.移位寄存器

一个移位寄存器(Shift Register)就是一个触发器链,允许数据在一个固定的延迟段之间传递,也叫静态移位寄存器。其通常包括:时钟信号、可选的时钟使能信号、串行数据输入和输出。

Vivado综合可以用SRL类型的资源实现移位寄存器,如SRL16ESRLC32E。根据移位寄存器的长度,综合时会选择采用一个SRL类型原语实现,或采用级联 的SRLC类型原语实现。下面给出移位寄存器的代码示例:

// 32Bits移位寄存器,上升沿时钟,高电平有效时钟使能信号
// 使用连接运算符{}实现
module shift_registers_0 (input clk, clken, SI, output SO
);parameter WIDTH = 32;
reg [WIDTH-1:0] shreg;always @(posedge clk)if (clken) shreg = {shreg[WIDTH-2:0], SI};assign SO = shreg[WIDTH-1];endmodule// 使用for循环实现
module shift_registers_0 (input clk, clken, SI, output SO
);parameter WIDTH = 32;
reg [WIDTH-1:0] shreg;integer i;
always @(posedge clk)if (clken) beginfor (i = 0; i < WIDTH-1; i = i+1)shreg[i+1] <= shreg[i];shreg[0] <= SI;endassign SO = shreg[WIDTH-1];endmodule

4.动态移位寄存器

动态移位寄存器(Dynamic Shift register)是指电路操作期间移位寄存器的长度可以改变。可以采用如下两种结构实现:

  • 一组触发器链,最大长度可以改变;
  • 一个多路选择器,在给定时钟周期选择从传递链中提取数据的某一段。结构框图如下图所示:

Verilog示例代码如下所示:

// 32-bit 动态移位寄存器
module dynamic_shift_register_1 (CLK, CE, SEL, SI, DO);parameter SELWIDTH = 5;
input CLK, CE, SI;
input [SELWIDTH-1:0] SEL;
output DO;localparam DATAWIDTH = 2**SELWIDTH;
reg [DATAWIDTH-1:0] data;always @(posedge CLK)if (CE == 1'b1) data <= {data[DATAWIDTH-2:0], SI};assign DO = data[SEL];  endmodule

5.乘法器

综合工具从源代码中的乘法运算符来推测是否使用乘法器。乘法运算结果的位宽为两个操作数位宽之和。比如16Bits的信号乘以8Bits的信号,结果为24Bits。乘法器可以用Slice单元或DSP块实现,选择依据有两点:(1).操作数的大小;(2).是否需要最佳性能。通过第25篇介绍过的USE_DSP属性可以强制设定乘法器的实现方式,设置为no用slice实现;设置为yes用DSP块实现。

当使用DSP块实现乘法器时,Vivado综合可以发挥DSP块流水线能力的最大优势,综合时会在乘法操作数和乘法器后插入两级寄存器。当乘法器无法用一个DSP块实现时,综合时会拆分乘法运算,采用几个DSP块DSP块加slice的方案实现。下面给出代码示例:

// 16*24-bit乘法器,操作数一级延迟,输出三级延迟
module mult_unsigned (input clk, input [15:0]A, input [23:0]B, output [39:0]RES
);reg [15:0] rA;
reg [23:0] rB;
reg [39:0] M [3:0];integer i;
always @(posedge clk)
beginrA <= A;rB <= B;M[0] <= rA * rB;for (i = 0; i < 3; i = i+1)M[i+1] <= M[i];
endassign RES = M[3];endmodule

除了乘法器,综合工具还可以通过乘法器、加法器/减法器、寄存器的使用,推断出乘加结构(Multiply-Add)、乘减结构(Multiply-Sub)、乘加减结构(Multiply-Add/Sub)和乘累加结构(Multiply-Accumulate)。


5.复数乘法器

下面给出一个复数乘法器的代码示例,该设计会使用三个DSP48单元:

// 复数乘法器(pr+i.pi) = (ar+i.ai)*(br+i.bi)
module cmult # (parameter AWIDTH = 16, BWIDTH = 18)
(input clk,input signed [AWIDTH-1:0] ar, ai,input signed [BWIDTH-1:0] br, bi,output signed [AWIDTH+BWIDTH:0] pr, pi
);reg signed [AWIDTH-1:0] ai_d, ai_dd, ai_ddd, ai_dddd ;
reg signed [AWIDTH-1:0] ar_d, ar_dd, ar_ddd, ar_dddd ;
reg signed [BWIDTH-1:0] bi_d, bi_dd, bi_ddd, br_d, br_dd, br_ddd ;
reg signed [AWIDTH:0] addcommon ;
reg signed [BWIDTH:0] addr, addi ;
reg signed [AWIDTH+BWIDTH:0] mult0, multr, multi, pr_int, pi_int ;
reg signed [AWIDTH+BWIDTH:0] common, commonr1, commonr2 ;always @(posedge clk)
beginar_d <= ar;ar_dd <= ar_d;ai_d <= ai;ai_dd <= ai_d;br_d <= br;br_dd <= br_d;br_ddd <= br_dd;bi_d <= bi;bi_dd <= bi_d;bi_ddd <= bi_dd;
endfinal products
//
always @(posedge clk)
beginaddcommon <= ar_d - ai_d;mult0 <= addcommon * bi_dd;common <= mult0;
end
// Real product
//
always @(posedge clk)
beginar_ddd <= ar_dd;ar_dddd <= ar_ddd;addr <= br_ddd - bi_ddd;multr <= addr * ar_dddd;commonr1 <= common;pr_int <= multr + commonr1;
end
// Imaginary product
//
always @(posedge clk)
beginai_ddd <= ai_dd;ai_dddd <= ai_ddd;addi <= br_ddd + bi_ddd;multi <= addi * ai_dddd;commonr2 <= common;pi_int <= multi + commonr2;
endassign pr = pr_int;
assign pi = pi_int;endmodule

6.DSP块中的预加器

当使用DSP块时,设计代码最好使用带符号数(signed)运算,这样需要一个额外的bit位宽来存储预加器(pre-adder)结果,这一位也可以封装在DSP块中。一个动态配置预加器(后面带一个乘法器和加法器)的Veirlog示例如下:

// 控制信号动态选择预加或预减
module dynpreaddmultadd # (parameter SIZEIN = 16)
(input clk, ce, rst, subadd,input signed [SIZEIN-1:0] a, b, c, d,output signed [2*SIZEIN:0] dynpreaddmultadd_out
);reg signed [SIZEIN-1:0] a_reg, b_reg, c_reg;
reg signed [SIZEIN:0] add_reg;
reg signed [2*SIZEIN:0] d_reg, m_reg, p_reg;always @(posedge clk)if (rst) begina_reg <= 0; b_reg <= 0;c_reg <= 0; d_reg <= 0;add_reg <= 0;m_reg <= 0; p_reg <= 0;endelse if (ce) begina_reg <= a; b_reg <= b;c_reg <= c; d_reg <= d;if (subadd)add_reg <= a - b;elseadd_reg <= a + b;m_reg <= add_reg * c_reg;p_reg <= m_reg + d_reg;end// 输出累加结果
assign dynpreaddmultadd_out = p_reg;endmodule

7.UltraScale DSP块中的平方电路

UltraScale DSP块的原语DSP48E2支持计算输入或预加器输出的平方。下面的示例代码计算了差值的平方根,该设计会使用一个DSP块实现,且有最佳的时序性能:

// DSP48E2支持平方运算,预加器配置为减法器
module squarediffmult # (parameter SIZEIN = 16)
(input clk, ce, rst,input signed [SIZEIN-1:0] a, b,output signed [2*SIZEIN+1:0] square_out
);reg signed [SIZEIN-1:0] a_reg, b_reg;
reg signed [SIZEIN:0] diff_reg;
reg signed [2*SIZEIN+1:0] m_reg, p_reg;always @(posedge clk)if (rst) begina_reg <= 0;b_reg <= 0;diff_reg <= 0;m_reg <= 0;p_reg <= 0;endelse if (ce) begina_reg <= a;b_reg <= b;diff_reg <= a_reg - b_reg;m_reg <= diff_reg * diff_reg;p_reg <= m_reg;endassign square_out = p_reg;endmodule

8.黑盒子

FPGA设计可以包含EDIF网表,这些网表必须通过实例化与其它设计部分连接在一起。在HDL源代码中使用BLACK_BOX属性完成实例化,这部分实例化还可以使用一些特定的约束。使用BLACK_BOX属性后,该实例将被视作黑盒子。下面给出示例代码:

//模块定义
(* black_box *) module black_box1
(input in1, in2, output dout
);endmodule//模块实例化
module black_box_1
(input DI_1, DI_2, output DOUT
);black_box1 U1 (.in1(DI_1),.in2(DI_2),.dout(DOUT)
);endmodule

9.FSM状态机

默认情况下,Vivado综合可以从RTL设计中提取出有限状态机(FSM),使用-fsm_extraction off可以关闭该功能。通常需要设计者设置FSM的编码方式,便于综合时根据设置调整优化目标。

Vivado综合支持Moore和Mealy型状态机。一个状态机由状态寄存器、下一个状态功能、输出功能三部分组成,可用如下框图表示:

Mealy状态机需要从输出到输入的反馈路径。尽管状态寄存器也支持异步复位,但最好还是使用同步复位方式。设置一个复位或上电状态,综合工具即可识别出FSM。默认状态编码为auto,综合时会选择最佳的编码方式:

  • 独热码(One-Hot):最多支持32个状态,有最快的速度和较低的功耗,每个状态由一个独立的bit(对应一个触发器)表示,状态间转换时只需要改变两个bit的状态。
  • 格雷码(Gray):两个连续状态之间转换时只需要改变一个Bit的状态,可以最小化冒险和毛刺现象,有最低的功耗表现。
  • Johnson编码:适用于包含没有分支的长路径的状态机;
  • 顺序编码:使用连续的基数值表示状态,跳转到下一个状态的状态方程最简单。

下面给出一个FSM的Verilog示例:

// 顺序编码的FSM
module fsm_1(input clk, reset, flag,output reg sm_out
);parameter s1 = 3'b000;
parameter s2 = 3'b001;
parameter s3 = 3'b010;
parameter s4 = 3'b011;
parameter s5 = 3'b111;reg [2:0] state;  //状态寄存器always@(posedge clk)if(reset) beginstate <= s1;sm_out <= 1'b1;endelse begincase(state)s1: if(flag) beginstate <= s2;sm_out <= 1'b1;endelse beginstate <= s3;sm_out <= 1'b0;ends2: begin state <= s4; sm_out <= 1'b0; ends3: begin state <= s4; sm_out <= 1'b0; ends4: begin state <= s5; sm_out <= 1'b1; ends5: begin state <= s1; sm_out <= 1'b1; endendcaseendendmodule

10.ROM设计方法

Read-only memory(ROM)使用HDL模型实现与RAM非常相似。使用ROM_STYLE属性选择使用寄存器或块RAM资源来实现ROM。使用块RAM资源实现ROM的示例代码如下:

//使用块RAM资源实现ROM
module rams_sp_rom_1 (input clk, en, input [5:0] addr, output [19:0] dout
);(*rom_style = "block" *) reg [19:0] data;always @(posedge clk)if (en)case(addr)6'b000000: data <= 20'h0200A; 6'b100000: data <= 20'h02222;6'b000001: data <= 20'h00300; 6'b100001: data <= 20'h04001;6'b000010: data <= 20'h08101; 6'b100010: data <= 20'h00342;......6'b011110: data <= 20'h00301; 6'b111110: data <= 20'h08201;6'b011111: data <= 20'h00102; 6'b111111: data <= 20'h0400D;endcaseassign dout = data;endmodule

Vivado使用技巧(26):HDL编写技巧相关推荐

  1. web前端好学吗?如何能提高CSS编写技巧 提高Web前端开发效率

    前端开发学习每个人都想走捷径,即想高效快速学习又想掌握当今最新的前端技术知识.其实,这样的想法是对的,找对学习方法就能达到这个效果.前端开发学习每个阶段都是非常重要的,一部分为专业知识.另一部分实战经 ...

  2. 亚马逊运营知识:ASIN文案编写技巧

    ASIN页面是买家了解商品的重要渠道,对转化率的影响很大,它包括了图片.价格.评论.文案等.那么ASIN文案怎么编辑呢?下面海熹跨境人才网给大家分享亚马逊运营人才ASIN文案编写技巧.一起来了解一下吧 ...

  3. LaTeX中一些常用符号及编写技巧

    博客中阅读效果更佳哦:LaTeX中一些常用符号及编写技巧 希腊字母 小写形式 代码 大写形式 代码 α \alpha A \Alpha β \beta B \Beta δ \delta Δ \Delt ...

  4. 动量策略编写技巧----策略编写学习教材

    动量策略编写技巧----策略编写学习教材(三) 一.本章内容主要介绍了如何在MINDGO平台上快速学会编写动量策略,希望能给有需要的小伙伴带来一定帮助. 本文建立于多因子策略编写技巧之上,因此需要完整 ...

  5. 速卖通描述 html,产品详细描述编写技巧-速卖通工作日记3

    产品详细描述编写技巧-速卖通工作日记3 本文发表于2014年3月,现在看来这篇文章还是实用的. 在16号的时候出单我的速卖通第一笔订单,虽然金额不大但是对自己是一个非常大的鼓励.这两天一直在研究产品的 ...

  6. Qt实用技巧:VS2017编写纯C库以及使用Qt调用C库方法

    若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 原博主博客导航:https://blog.csdn.net/qq21497936/ ...

  7. Linux编写脚本nsum求和,详解Linux Shell脚本编写技巧,附实例说明

    原标题:详解Linux Shell脚本编写技巧,附实例说明 Linux Shell是一个很难的知识板块.虽然大家都认真学,基本的语法很都掌握,但有需求时,很难直接上手编程,要么写了很久,要么写不好!对 ...

  8. X86逆向教程15:OD脚本的编写技巧

    本章节我们将学习OD脚本的使用与编写技巧,脚本有啥用呢?脚本的用处非常的大,比如我们要对按钮事件进行批量下断点,此时使用自动化脚本将大大减小我们的工作量,再比如有些比较简单的压缩壳需要脱壳,此时我们也 ...

  9. 很实用的一本书-《微软Office技巧大赛优秀作品--Office技巧1000例》

    星期六去图书馆的时候在一个不起眼的角度看到了这本看样子好旧的书(事实上也很旧,2002年出版的).也许很多人会觉得我借这本书很幼稚,不过我确实觉得IT从业人员了解Office的一些技巧对文档的编写工作 ...

最新文章

  1. 利用RGB-D数据进行人体检测 带dataset
  2. python怎么在电脑上使用-使用python在本地电脑上快速处理数据
  3. 自定义CSS博客皮肤
  4. 2信号处理之:信号产生原因,进程处理信号行为,信号集处理函数,PCB的信号集,sigprocmask()和sigpending(),信号捕捉设定,sigaction,C标准库信号处理函数,可重入函数,
  5. Java最大公共子串(动态规划)
  6. C++来了,详细知识点思维导图!
  7. SharePoint2013升级SP1后,运行配置向导报错:未注册sharepoint服务
  8. Java练手项目-王者荣耀项目源码分享!
  9. 0ffice2003安装2007兼容包不能使用的解法
  10. windows下用linux系统,如何在Windows下使用Linux操作系统
  11. VS 2013编译xvid
  12. python input函数换行_python input函数换行_python_input函数
  13. 德鲁克的时间管理法—《可以量化的…
  14. 垃圾回收算法——复制算法 以及eden和survivor
  15. 火力发电厂的基本生产过程
  16. P50发布,鸿蒙OS用户突破4000万!
  17. C语言全局变量(c文件和h文件中的全局变量、静态全局变量)使用注意事项
  18. ArcBlock 分享 | 融合云计算,才是区块链的未来
  19. 云计算 刘鹏 第二版 pdf共享
  20. 急性心机梗护理查房PPT模板

热门文章

  1. 100多个新媒体人实用网站
  2. 44session绑定解绑、钝化活化
  3. python之批量下载网页数据
  4. ORA-01012: not logged on 解决办法记录错误
  5. vue使用 moment.js 格式化时间(获取当前日期的周一和周日)
  6. 以下不是python语言合法变量_违法行为的客体是指法律所保护的而为违法行为所侵害的:()...
  7. python写一个飞花令程序
  8. 企业中流砥柱:别让企业中层缺位
  9. cesium获取模型高度_180套经典夹具设计方案(附详解+模型),原来夹具设计这么简单!...
  10. C#获取企业微信打卡数据