1 HDL基本介绍

Hardware Description Language (HDL)。

DSP:并行,一般最多三条语句同时执行。——由硬件内部结构决定。考虑GPU的使用即为CPU难以完成多个ALU(运算器)的处理,而GPU可以有非常多的ALU(相当于CPU为工程师,而GPU为普通工人)。

CPLD与FPGA的最大区别(为什么一个是乘积项一个是查找表):CPLD的触发器工艺做不上去,因此主要用于组合逻辑;而FPGA集成了大量的RAM(SRAM),因此适合做时序逻辑。

时钟的最大频率由逻辑的时延决定(可编程逻辑器件最后一个题考的就是D触发器的最大时钟频率公式)。

FPGA重点在于实时处理能力:CPU进行一个简单的处理操作可能就要花费100多个时钟周期。

硬件直接执行的语言和硬件描述语言的区别:一个是执行“软件”,一个是“软件”描述“硬件”。硬件描述语言可称为:“面向接口编程”。(记得本科的JAVA老师提过除了熟悉的面向过程、面向对象,还有面向接口、面向方法,而很多学校已经在本科讲后面两种了)

多时钟适用于CPLD(如时钟取反),不适用于FPGA(同步时钟)。

语言:将外部事物映射到现有基本单元和处理规则体系下。

代码要符合逻辑关联,表述硬件信号逻辑。(而非凑时序)

2 VHDL语言

  • VHDL :超高速集成电路硬件描述语言 Very-High-Speed Integrated Circuit Hardware Description Language。现有主要版本:VHDL-1993。
  • 基本代码结构:(1)实体entity(二级单元:结构体architecture)(2)程序包package(二级单元:包体package body)(3)配置声明(4)上下文声明。基本单元能够独立存在,二级单元必须先定义其基本单元才能定义。
  • 结构体主体:1 进程 2 信号赋值 3 元件例化 4 生成语句 5 块语句(不常用)
    在这里插入图片描述
    在这里插入图片描述

库和实体

*程序包 package:声明了可以被电路各部分使用的数据类型、子程序、运算和元件等对象。包 体 package body:给出了程序包中声明的子程序和运算的具体实现。
*常用库:(1)std 库: VHDL 标准库的一部分。默认下列语句存在, 不要显式重复声明:Library std; Use std.standard.all;(2)ieee 库,包含了一组可用于 RTL 综合的标准逻辑类型和数据类型的程序包(3)work 库,包含了当前设计工程中的所有设计单元编译结果。默认存在, 引用时不需要显式使库可见。

  • 实体entity:定义了电路的接口和名称;结构体architecture:定义了电路的具体实现。

数据对象和数据类型:

*三种数据对象:常量、变量、信号。

  • 变量是中间关联关系,
  • 变量(定义)赋值:分配给变量的值立刻成为当前值,不能设置传输延迟variable 变量名 : 数据类型 [:= 初始值];
    信号赋值:如果 一 个进程中多次为同一个信号赋值,只有最后一次的赋值会起作用。signal 信号名 : 数据类型 [:= 初始值];
  • 有效的硬件初始值必须通过统一的复位逻辑来实现!
  • 条件信号赋值:sum3<=a+b when sel ==‘1’ else a-b; 各赋值语句有优先级的差别,赋值条件可以重叠。
    选择信号赋值:with sel select
    dout<=d0 when “00”, d1 when “01”, d2 when “10”, d3 when “11”, “00000000” when others; 选择值必须互斥,不能出现重复或重叠。
  • record记录:元素组成的集合,类似C中的结构体。在VHDL和Verilog混合编程中的project不建议使用record。
  • 字符类型可综合。
  • 16#E#E1:16进制的14e1=14*16=224。

操作符:

  • 标准逻辑位std_logic:只有“0”、“1”、“Z”(高阻态)可综合。
  • 移位操作符可综合。(移位器)乘法器和除法器可综合,乘方原则上不可综合。移位器使用要优于乘法器(*2/4/8)。求模取余也可综合。
    *VHDL没有按位与、按位或。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结构体之进程:

在这里插入图片描述

  • 注意敏感信号参数表的填写!!!若无,则必须包含wait语句(互斥)。

  • 一个实体可以有多个结构体,每个结构体描述了同一个电路硬件部件的不同实现。

  • 1进程PROCESS是一系列必须按时序执行的时序语句。2与软件程序的区别:重复运行,连续循环。3同一个结构体内的多个进程之间是并行执行的关系。

  • 如果没有敏感列表,进程中必须包含 wait 语句来暂停进程;如果有敏感列表, wait 语句 不能出现在进程中。
    在这里插入图片描述

  • 声明对进程是局部的,只能用于进程内。

  • 仿真器对于进程的执行:进程执行过程中所有信号值不变–如果进程过程中有对信号的赋值,将改变信号值的事件排入等待执行的队列–进程结束执行时,执行排在队列中的赋值事件。

  • 进程中的信号赋值:同一个进程中对同一个信号多次赋值,只有最后一次生效。在不同进程中,不能对同一个信号进行赋值。在进程中只能使用简单赋值。
    在这里插入图片描述

  • 流程控制语句:if 和 case 语句替代条件赋值和选择赋值;loop 语句:循环控制。

  • 1初始值没有硬件解释,综合器会忽略初值2进程执行期间,信号不能用于存储计算的中间值,必须使用变量3赋值语句立即更新变量

  • 需要避免的锁存latch,两种情况:1如果目标信号在某些条件下没有接收数值,则保留先前值:(1)缺少了对应于 if 语句的 else 部分(2)if 语句的某些分支没有对信号进行赋值。2先前值来自反馈:组合逻辑会引入latch。时序逻辑是允许的。
    在这里插入图片描述

  • 时序进程:进程仅在每个时钟的上升沿(下降沿)启动。

  • 时钟门控:时钟信号由其他控制信号开启或关闭。如果没有特殊需求,不要使用时钟门控,容易出现毛刺。防毛刺可以通过一个寄存器的锁存输出。数据门控:门控控制寄存器的数据输入。
    在这里插入图片描述

VHDL语法 :

在这里插入图片描述

  • 结构体之元件例化:将预先设计好的实体定义为元件,并将其与当前设计实体中的端口连接。
  • 结构体之生成语句(重复语句):用于创建重复硬件结构或者条件硬件结构–for generate :用于重复硬件结构–if generate :用于条件硬件结构。并发语句,能用于任何结构体。
  • 函数和过程:函数(function)和过程(procedure)统称为子程序(subprogram)。子程序中不允许使用wait 语句。函数中禁止信号声明和元件例化。procedure一般可用process代替,尽量不推荐使用。测试testbench时建议用。
    在这里插入图片描述

VHDL语法规则及注意事项:


1.命名规则:不区分大小写;不允许出现两个连续的下划线,***注释由两个连续虚线(‘–’)开始直至行尾;注意in、out是关键字。
2.port的最后一句不加分号!!!(编译出现多条错误时首先检查该项)
3.entity名要与 VHDL 程序的文件名一致。
4.“:=”表示预编译时赋值,“<=”表示真实赋值。(注意赋值区分,结果会不同)
5.“when···else”等选择语句一定要穷举所有可能性!(否则出现锁存latch)
6.等于“=”,不等于“/=”。
7.VHDL是一种强类型语言,不支持类型复用。
8.向量测试:常用AAAA(A 1010),5555(5 0101)测试焊接数据线短路问题。
9.信号类型:尽量只用std_logic、std_logic_vector。
10.不要在设计中同时使用异步(RST在时序clock外)复位和异步置位。
11.elsif而非elseif!!!要有end if!!!
12.极力避免组合逻辑!!!latch!!!(以及选择涵盖所有分支)

3 Verilog语言

基本:

  • 区分大小写
  • 单行注释以“//”开始,多行注释以“/*”开始,以 “*/”结束。
  • Verilog 中关键字全部为小写。
  • 四种基本电平逻辑:0,1,未知x或X,高阻z或Z。
  • 数据类型:1.线网net:表示硬件单元之间的物理连线,由其连接的器件输出端连续驱动。一般使用wire声明,还有wand,wor,tri等。如果没有驱动元件,缺省值一般为 z。 2.寄存器:用来表示存储单元。会保持数据原有的值,直到被改写,不需要驱动源。默认值为 x。仅仅意味着一个保持数值的变量,不代表硬件寄存器。 3.wire 或 reg 可声明为向量(位宽大于 1。格式:[high#: low#]或 [low#: high#],左边的数总是代表向量的最高有效位。起始位可以是变量,位宽必须是常量。
  • 整数、实数和时间等数据类型实际也属于寄存器类型。
  • reg 型变量为无符号数, integer 型变量为有符号数。
  • 时间寄存器:时间变量通过使用关键字 time 来声明。通过调用系统函数 $time 可以得到当前的仿真时间。单位受`timescale 定义的参考时间单位控制。`timescale 1ns/1ps:时延单位1 ns,时延精度1 ps。(电脑左上角撇号)。用于仿真,不可综合。惯性延迟,小于一个周期保持原来参数。
  • 字符串保存在 reg 类型的变量中,每个字符占用一个字节(8bit)。
    在这里插入图片描述
    在这里插入图片描述

模块和端口:

  • 模块是设计中的基本功能块,端口是模块与外部交互的通道。
  • 所有端口隐含地声明为wire类型。不能将 input 和 inout 类型的端口声明为 reg 类型,output 类型的端口可以声明为 reg 类型,端口数据类型为 reg 时, reg 声明不能省略。
    在这里插入图片描述
    在这里插入图片描述

数据流建模:

  • 连续赋值语句:以关键字 assign 开始,用于对线网进行赋值。不可综合!
  • 指定延迟写法示例:wire #10 out; 注意:在延迟期间(左侧获得新值之前),如果右侧信号再次发生变化,在计算表达式新值时会取右侧信号的当前值(惯性延迟)

行为级建模

  • 结构化过程语句:行为级建模的两种基本语句:initial 、 always。initial 语句从0时刻开始执行,只执行一次,多个initial块之间是相互独立的。理论上不可综合,类似于VHDL的process。 always 语句结构是重复执行的。
  • 过程赋值语句,存在两种:–阻塞赋值语句(=),顺序执行。–非阻塞赋值语句(<=),并行执行。仿真时的时序控制结果不同。

基于事件的时序控制

  • –常规事件控制;命名事件控制;OR (或)事件控制;电平敏感时序控制。可以用来触发声明语句或块语句的执行。
  • 使用符号@来说明。语句执行条件:信号值发生变化、发生正向跳变posedge、发生负向跳变negedge。
  • 由关键字 or 连接多个事件名或者信号名构成敏感列表。多个信号或事件中发生的任意一个变化都能够触发语句或语句块的执行。关键字 or 也可以使用“,”来代替。组合逻辑块语句的输入变量很多时,可以用@*或@(*),表示对其后语句块中的所有输入变量的变化是敏感的。
  • 在同一个always 块中 不要既用非阻塞赋值又用阻塞赋值。时序电路建模时,使用非阻塞赋值。锁存器电路建模时,使用非阻塞赋值。用 always 块建立组合逻辑模型时,使用阻塞赋值。

主要语句

  • 条件语句:if-else;case(结尾加end或endcase)

  • 循环语句四种类型:while for repeat forever。只能在 always 或 initial 块中使用。

  • 块语句:1顺序块:用关键字 begin 和 end 来表示。顺序块中的语句是一条接一条按顺序执行。2并行块:由关键字 fork 和 join 声明。并行块内的语句并发执行。语句执行的顺序由各自语句中的延迟或事件控制决定。

  • 子程序:任务(task)和函数 (function)。可以在任务和函数中声明局部变量,如寄存器、时间、整数、实数和事件,但不能声明线网类型的变量;只能使用行为级语句,但不能包含 always 和initial 块;可以在 always 块、 initial 块中调用任务和函数。
    在这里插入图片描述

  • 函数不含有非阻塞赋值语句,综合成纯组合逻辑。

  • 编译指令:`define,`undef(键盘左上角)。条件编译:`ifdef `ifndef `elsif `else `endif。

  • 系统任务:display打印;strobe语句结束后才打印。

注意事项:


1.时间寄存器`timescale电脑左上角撇号!!!
2.在同一个always 块中 不要既用非阻塞赋值又用阻塞赋值。时序电路建模时,使用非阻塞赋值<=。
3.if-else关注配对问题。
4.FPGA跳到不可能状态:(1)逻辑写法的latch:信号不完备,输出端引入到输入端的反馈!(2)时序逻辑电路建模不小心写成阻塞赋值=。
5.状态机建议选用case(综合时无先后顺序),不要用if(有先后顺序)。
6.for进行判断中判断语句不能用=或~=。(Verilog里要求)

4 综合和仿真

基本:

  • 综合(Synthesis):将较高级抽象层次的设计描述自动转化为较低层次描述的过程。
  • 编程(Program)配置 (Configuration):将适配后生成的编程文件装入 PLD 器件中的过程。
  • 大小写敏感性:1当仅仅用大小写区分两个不同的实例或信号时,综合器可以正常识别。2当仅仅用大小写区分两个不同的模块( module )时综合器可能会报错!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

有限状态机

  • 分类:Mealy状态机、Moore状态机、带有流水线的Mealy 状态机。
  • 使用 parameter verilog 或枚举 enum VHDL 类型定义状态。避免直接使用数值定义状态。避免使用数值计算定义状态跳转。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

testbench

  • 在综合后和布局布线后仿真中,全局复位 置位信号( Global Set/Reset, GSR )默认持续 100ns。
  • 自动验证技术和自检查测试文件:常见的自动验证方法,1数据文件比较:将仿真结果的期望值写入一个文件,将仿真结果写入另一个文件,在仿真结束后比较两个文件进行验证。2波形比较:某些仿真软件支持输出波形和期望波形的自动比较。 自检查测试文件:将期望结果作为测试文件的一部分,在仿真过程中实时完成结果的比较。

与其他软件的混合开发

  • MyHDL python http://www.myhdl.org
  • cocotb python https:// github.com/cocotb/cocotb
  • Chisel scala https:// github.com/chipsalliance/chisel3
  • SpinalHDL scala https:// github.com/spinalhdl/spinalhdl

注意事项:


1.建议不要使用未指定宽度的整数,有可能产生不确定的问题(和综合器有关)。
2.不要使用异步方式置位或复位寄存器。
3.对于时钟使能,set/reset,尽量使用高电平有效。低电平有效会综合成额外的非门。
4.人为增加信号延迟:(1)时钟锁-动态移位寄存器:对高频延迟控制多个时钟周期,可采用移位寄存器。(大延迟)(2)调用FPGA原语(小延迟)
5.RAM使用初始化条件:请使用$readmemb,不要用$readmemh。
6.ROM只有读接口,initial语句可综合成ROM。
7.注意:timing error如果出现,请不要下载到FPGA中。

5 时序分析和时序控制

FPGA中的时序问题

1同步时序问题:过于复杂的组合逻辑,或逻辑的不适当设计,导致建立时间、保持时间的条件不能满足。对同步接口锁存造成的接口效率下降。
2跨时钟域问题:由于跨时钟域造成的亚稳态,使得同一个输出在不同的接收寄存器上被解释成不同的值造成的逻辑错误。同一组信号输出由于亚稳态造成的时间延迟不同,在接收端引起的数据接收错误。

同步时序控制的基本策略

1 建立时间、保持时间的条件不满足(无握手)
(1)同源时钟问题
如果不加特殊约束设置,分频时钟不能利用FPGA全局时钟树,到达各个寄存器的延迟相差太大,影响时序性能。解决方法:为分频时钟增加额外的时序约束;使用PLL+全局时钟buffer产生分频时钟,使分频时钟成为全局时钟;不要直接使用分频后时钟(最好方法)
(2)流水线技术
将原本一个时钟周期完成的较大的组合逻辑通过合理的切割后分由多个时钟周期完成。使用条件:处理流程可以分为若干步骤;整个数据处理是单向的,没有反馈或迭代运算。
(3)组合逻辑优化:

  • 查找表技术:在FPGA中,对于某些特定问题,直接建立输入信号和输出结果之间的查找表,避免使用复杂组合逻辑通过运算产生结果,是有效的时序优化技术。运算中含有复杂计算时;算法比较复杂,但输入和输出信号简单时。
  • 8b/10编码器
  • CRC编码

2 对同步接口锁存造成的接口效率下降(接口的读和写)

  • 产生一个额外的时钟周期,延迟可能产生数据问题。(使用提前控制信号以及变种!)
    (1)适当的组合逻辑
    (2)同步接口流水线
  • 使用提前控制信号。
  • 预读技术的变种:FWFT FIFO,First-Word Fall-Through FIFO,预读一个数,保证后续的读信号可以实时输出数据。
    在这里插入图片描述

(3)同步接口寄存器隔离

  • 在同步接口设计中,通过额外的缓存寄存器实现所有接口信号的寄存器化(FIFO读接口,让rd_en信号寄存器化)

跨时钟域(CDC)逻辑设计

  • 1 由于跨时钟域造成的亚稳态,使得同一个输出在不同的接收寄存器上被解释成不同的值造成的逻辑错误。
  • 2 同一组信号输出由于亚稳态造成的时间延迟不同,在接收端引起的数据接收错误。
    两种场景

场景1:允许在不同时钟域之间缺失采样边沿,每个采样值必须是准确的。异步FIFO的读写地址。
场景2:跨时钟域的每个信号必须被采样,信号必须确认被正确接收后才能改变

  • 解决方式:同步寄存器链。(“锁两级”)
    对某些高速应用,两级触发器锁存并不足以保证足够好的MTBF性能,需要三级或更多级的同步寄存器链。
  • 跨时钟域时的可靠传输条件:由慢时钟域向快时钟域传输时的安全条件,“三边沿”条件。当通过两级触发器来进行跨时钟域信号传输时,当CDC信号宽度为接收时钟域时钟周期的1.5倍以上时,可以保证信号被接收端至少正确接收一次。以上条件等效为:CDC信号应至少持续3个接收时钟边沿来保证被正确接收。
  • 同步寄存器链使用条件:1必须是从慢时钟域到快时钟域的传输 2必须满足三边沿条件

跨时钟域多路信号处理

  • 简单的二级寄存器链不能保证信号的正确传输:不同信号之间有着传输延迟,并不能保证所有信号同时被同一个时钟采样!
  • 可能采取的策略:1多位信号合并:如果可能,将多个CDC位合并成1个CDC信号 2多周期路径(MCP)方式:使用同步的load位安全传递多位信号 3异步FIFO方式(格雷码计数器等,仅有一位发生变化)

注意事项:


1.命名常见规则:_p/_n(高/低电平有效)
2.晶振jitter一般10 ps,高精度可做到ps以下。ps以下可直接做高速串行收发器。在外部锁相环锁过的信号不能直接驱动高速信号收发器(相噪大)。
3.同步接口锁存:使用提前控制信号以及变种!
4.跨时钟域(CDC):同步寄存器链。(“锁两级”)

6 模块化设计和片上总线

层次化设计方法

  • 真实设计时以 Top down 方法为主,以 Bottom up 方法为辅。
  • IP:IC 设计和 FPGA 设计中指完成某种功能的设计模块。硬核:以版图形式实现的设计模块,通常用GDS II文件格式。软核:在寄存器级或门级对电路功能用 HDL 进行描述,表现为 Verilog 或 VHDL 代码的形式。固核:完成了综合的功能块,通常以网表的形式提交给客户使用。
    在这里插入图片描述

片上总线( On Chip Bus, OCB )

  • 以总线方式实现 IP 核之间的数据通信。需要定义各个模块之间初始化、仲裁、请求传输、响应、发送接收过程中的驱动、时序、策略等关系。
    标准片上总线: AMBA ( ARM )、 Wishbone ( Silicore )、 Avalon ( Altera )等
  • I2C 是一种串行总线的外设接口,它采用同步方式串行接收或发送信息,两个设备在同一个时钟下工作。I2C 总线只用两根线:串行数据 SDA Serial Data、串行时钟SCL Serial Clock。I2C 总线器件没有片选控制线,所以 I2C 总线数据传输的开始必须由主器件产生通信的开始条件( SCL 高电平时,SDA 产生负跳变);通信结束时,由主器件产生通信的结束条件( SCL 高电平时, SDA 产生正跳变)。
    在这里插入图片描述
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐