FPGA 设计语言
1 什么是硬件描述语言
硬件描述语言(HardWare Description Language,HDL),以文本形式来描述数字系统硬件结构和行为,是一种用形式化方法来描述数字电路和系统的语言。可以从上到下来逐层描述用户的设计思想。即用一系列分层次的模块来表示复杂的数字系统,并逐层进行验证仿真,再把具体的模块组合由综合工具转化成门级网表,接下去再利用布局布线工具把网表转化为具体电路结构的实现。
HDL语言的特征:
- HDL语言既包含一些高级程序设计语言的结构形式,同时也兼顾描述硬件线路连接的具体结构。
- 通过使用结构级行为描述,可以在不同的抽象层次描述设计。HDL语言采用自顶向下的数字电路设计方法,主要包括3个领域5个抽象层次。
- HDL语言是并行处理的,具有同一时刻执行多任务的能力。这和一般高级设计语言(例如C语言等)串行执行的特征是不同的。
- HDL语言具有时序的概念。一般的高级编程语言是没有时序概念的,但在硬件电路中从输入到输出总是有延时存在的,为了描述这一特征,需要引入时延的概念。HDL语言不仅可以描述硬件电路的功能,还可以描述电路的时序。
目前主流的HDL语言有Verilog HDL和VHDL,目前用的最多的是Verilog HDL。最新的IEEE关于Verilog的标准是IEEE.1364-2005。
需要说明的是:Verilog与 VHDL语言本身并没有什么优劣之分,而是各有所长。使用HDL语言描述数字硬件电路,HDL仅仅是将真实的硬件设计抽象为语言形式的工具,是真实的硬件电路到 EDA 工具的桥梁,选择何种语言作为桥梁本身并不重要,关键是如何有效地为真实电路建模,因此最重要的是建模的方法与思想。
HDL语言的适用层次示意图如下:

HDL语言和C语言的比较:
- 与C语言不同,HDL是描述而不是设计。任何电路结构的底层结构都可以看作是一系列逻辑门单元搭建起来的,我们可以用HDL去描述已存在的逻辑门,但是我们不可能用HDL凭空创造出新的逻辑门。
- C语言是运行在CPU平台上的,是串行执行的。HDL语言用于CPLD/FPGA开发,或者IC设计,对应着门逻辑,所有模块是并行执行的。
- 在Verilog学习中要理解什么是可综合的,什么是不可综合的。可综合成电路结构的Verilog语句是Verilog一个很小的子集,至于其他不可综合的Verilog语法更多是服务产生测试环境,为设计模块提供激励。
总结,HDL和C语言最大的区别体现在:互联,并发和时间。
2 Verilog HDL语言基础
2.1 描述层次与建模方法
使用Verilog HDL 语言可以从3个描述级别的5个抽象层次等不同层次描述数字电路系统,具体包括系统级、算法级、寄存器传输级(Register Transfer Level,RTL)、门级和开关级,如下表所示:
| 描述级别 | 抽象级别 | 功能描述 | 物理模型 |
|---|---|---|---|
| 行为级 | 系统级 | 用语言提供的高级结构能够实现所设计模块外部性能的模型 | 芯片、电路板和物理划分的子模块 |
| 行为级 | 算法级 | 用语言提供的高级功能能够实现算法运行的模型 | 部件之间的物理连接,电路板 |
| 行为级 | RTL级 | 描述数据如何在寄存器之间流动和如何处理、控制这些数据流动的模型 | 芯片、宏单元 |
| 逻辑级 | 门级 | 描述逻辑门和逻辑门之间连接的模型 | 标准单元布图 |
| 电路级 | 开关级 | 描述器件中三极管和存储节点以及它们之间连接的模型 | 晶体管布图 |
2.2 可综合与仿真特性
-
可综合语句的概念
综合就是将HDL语言设计转化为由与门、或门和非门等基本逻辑单元组成的门级连接。可综合语句是Verilog语言中可以被综合工具转换为电路结构的部分。它们是Verilog语言的子集,用于描述数字电路的行为和结构。
-
如何判断语句是否可综合
对于一段代码,如果设计者本身都不能想象出一个较直观的硬件实现方法,那EDA软件肯定也不行。例如加法器、多路选择器是大家都很熟悉的电路,所以类似
A+B-C,(A>B)?C:D这样的运算一定可以综合。而除法、求平方根、对数以及三角函数等较复杂的运算,必须通过一定的算法实现,并没有直观简单的实现电路,因而可以判断那些计算式是不能综合的,必须按其相应的算法写出更具体的代码才能实现。 -
仿真特性说明
Verilog最初设计用于电路描述和仿真,支持从行为级到门级的各种抽象层次。能够模拟电路的并发行为、时序和信号传播,帮助设计者在实际硬件实现前验证设计。Verilog的仿真是事件驱动的(Event-Driven Simulation),即当信号值发生变化时触发执行,这反映了硬件的并发性质。 以下是Verilog主要仿真特性的详细介绍:
特性类别 关键点描述 应用示例 事件驱动 基于信号变化触发,并发执行 时钟边缘敏感列表,例如,always @(posedge clk)块在时钟上升沿触发,适用于时序电路仿真。 时序模型 延迟、阻塞/非阻塞赋值,四值逻辑 传输延迟(Transport Delay)仿真,例如,使用#符号指定延迟,例如#5 c = b;表示等待5个时间单位后赋值。 建模风格 门级、数据流、行为级,可混合 测试台中的行为描述,例如,assign y = a & b;,适用于组合逻辑仿真。 系统任务 I/O、监控、文件操作、波形转储 调试打印和VCD生成,例如$display(“Time: %d, a: %b, b: %b, y: %b”, $time, a, b, y);,适用于调试信息输出。 测试支持 测试台、SDF定时、交互调试 功能验证和时序分析
2.3 设计模式
Verilog支持Top-Dowwn和Botton-up的设计方法。Top-Dowwn是从上往下设计,Botton-up是从下往上设计。
2.4 基本设计单元介绍
Verilog的基本设计单元是模块,模块由两部分组成:一部分描述接口,另一部分描述逻辑功能。在许多方面,电路模块和电路图符号是一致的,这是因为电路图符号的引脚也是程序模块的接口,程序则描述了这个电路图符号所实现的逻辑功能。
module xxx(
input xx,
output xx
);
endmodule
这是Verilog模块的示例,其结构位于module和endmodule之间,模块名称是模块的唯一标识符,建议模块名尽量用描述其功能的名字来命名。一个模块由端口定义、I/O说明,参数声明(可选)、内部信号声明和功能定义五个部分组成。
-
端口定义
- 端口定义是模块的输入和输出口名称,是它和别的模块端口联系的标识。模块的引用称为模块的实例化,模块的实例化有两种方式:
- 引用时,严格按照模块定义的端口顺序进行连接
- 引用时,用 . 符号,标明原模块定义时的端口名
-
I/O说明
- I/O说明是模块的输入和输出口的类型声明,用于声明模块的输入和输出口的类型。
- 输入口用 input 声明,输出口用 output 声明。
- 输入口和输出口的位宽必须声明。
-
参数声明(可选)
- parameter后面跟着一个逗号分开的赋值语句表,每一个赋值语句的右边必须是一个常数表达式,在模块的实例引用时,可以通过参数传递改变被引用模块或实例中已定义参数的值。
- 示例:
module RAM #( parameter WIDTH = 8, // 默认值 parameter DEPTH = 256 // 默认值 ) ( input [WIDTH-1:0] addr, output [WIDTH-1:0] data ); // 模块内部代码... endmodule // 实例化时覆盖参数 RAM #( .WIDTH(16), // 修改为16bit .DEPTH(1024) // 修改为1024深度 ) ram_inst ( .addr(addr_bus), .data(data_bus) ); //或者写成是 RAM ram_inst ( .addr(addr_bus), .data(data_bus) ); defparam ram_inst.WIDTH = 10; defparam ram_inst.DEPTH = 1024; -
内部信号声明
- 内部信号声明用于声明模块内部的信号,用于存储中间结果。
- 内部信号声明的格式为:
reg [WIDTH-1:0] signal_name;
2.5 三种描述方法
-
数据流描述:用 assign 语句,称为连续赋值语句
- 什么是数据流?在组合逻辑电路(输出只取决于当前时刻的输入)中,信号经过时类似数据流动,数据不会在中间存储。通过输入变化经过逻辑门延迟后总会体现在输出上
- 连续赋值语句特点
- 连续驱动:任何时刻输入的变化都会导致输出重新计算
- 只有 Wire 型变量才能在 assign 中赋值(仿真器不会存储连续赋值语句中的变量值)
- 使用 assign 语句对组合逻辑进行建模,符合组合逻辑的行为
- 并行性
-
行为描述:使用 always 或者 initial 语句
- 什么是行为描述?是用语言描述电路的行为,可以被理解为“当输入xx 信号发生 xx 变化时,输出 xx 信号怎么变化”
- 行为描述语句的格式
- Initial 和 always 后面跟语句或者与剧组。语句可以是非阻塞过程赋值或者阻塞过程赋值,连续过程赋值或者是高级编程语句;语句组可以是 Begin… End 和 Fork… Join 两种
- 时序控制
- 事件语句
@ - 延时语句
#,这种写法一般只用于仿真激励文件产生 - 等待语句,暂不考虑
- 事件语句
-
结构化描述:实例化已有的功能模块
以上三种方法可以在 Verilog 中产生逻辑,需要理解哪些过程是同时发生的,哪些过程是顺序发生的。以下四点极为重要:
- Verilog 中所有的过程块(initial 和 always 块),连续赋值语句 assign,实例引用语句都是并行的
- 它们描述的是一种通过变量名相互连接的过程
- 同一模块中三者的先后顺序没有关系
- 只有连续赋值语句 assign 和实例用引用语句可以独立于过程快而存在于模块的功能定义部分
3 Verilog HDL语言语法要素
- 标识符
- 关键字
- 数据类型
- 运算符
- 算术运算符:+、-、*、/、%
- 关系运算符:<、>、<=、>=、==、!=
- 逻辑运算符:&&、||、
!(逻辑取反) - 位运算符:&、|、^(按位异或)、~^(按位同或)、
~(按位取反) - 移位运算符:<<、>>
- 赋值运算符:=、<=
- 其他运算符:? :、->、->>、<<
- 位拼接运算符:{}
- 重复运算符:{n{expression}}
- 注释
- 内部信号