Verilog基础语法整理
Contents
研究生即将进行体系结构的学习,第一步自然是补一下欠缺的知识。这两天在寝室把Verilog 硬件描述语言的基础语法过了一遍,并将语法框架按照自己的理解稍微整理了一下,为日后起到提纲和构建知识体系之用。
名词概念
EDA工具:电子设计自动化(Electronic Design Automation)的简称,是从计算机辅助设计(CAD)、计算机辅助制造(CAM)、计算机辅助测试(CAT)和计算机辅助工程(CAE)的概念发展而来的。利用EDA工具,工程师将芯片的电路设计、性能分析、设计出IC版图的整个过程交由计算机自动处理完成。
软核:把功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的verilog HDL模型称之为“软核”。
固核:把在某一种现场可编程门阵列(FPGA)器件上实现的、经验证是正确的、总门数在5000门上电路结构编码文件称为“固核”。
硬核:把在某一种专用集成电路工艺的(ASIC)器件上实现的、经验证是正确的、总门数在5000门以上的电路结构版图掩膜称为“硬核”。
IP核:全称知识产权核(英语:intellectual property core),是在集成电路的可重用设计方法学中,指某一方提供的、形式为逻辑单元、芯片设计的可重用模组。IP核通常已经通过了设计验证,设计人员以IP核为基础进行设计,可以缩短设计所需的周期。
综合:综合是把verilog语言描述的抽象层次较高的设计描述转化成为抽象层次较低的电路网表,表现为一般的数字逻辑,能够对应到具体的门级逻辑。简言之,就是把用硬件描述语言描述的电路转换成实际能够实现的真实电路的过程。包括门级或者寄存器传输级甚至是开关级。
自顶向下的设计方式:从系统级开始,把系统划分为基本单元,然后再把每个单元划分为下一层次的基本单元,一直这样做下去,直到可以直接用EDA元件库中的基本元件来实现为止。
硬件设计阶段:建模、仿真、验证、综合。
模块结构
模块(block)是Verilog 的基本设计单元。一个模块的Verilog结构位于module和endmodule声明语句之间,一般由四部分组成,端口定义、IO说明、内部信号说明以及功能定义。后三者又可以概括为描述逻辑功能部分。
1 | // example block |
端口定义
1 | module module_name(port_1, port_2, ...); |
模块的端口表示的是模块的输入和输出口,即它与别的模块联系端口的标识。
引用方法
- 严格按照模块定义的端口顺序来连接。不需要标明原模块定义时规定的端口名。
- 在引用时用 “.” (点)符号来标明原模块定义的端口名。
IO说明
说明模块接口的输入/输出特性。分为输入口、输出口、输入/输出口三类。
输入口:
1 | input [width-1: 0] port_1; |
输出口:
1 | output [width-1: 0] port_4; |
输入输出口:
1 | inout [width-1: 0] port_7; |
IO说明也可以与端口声明写在一起。
内部信号声明
声明在模块内用到的和端口有关的额wire和reg类型变量。
1 | reg[width-1: 0] r_val_1, r_val_2, ... ; |
功能定义
用“assign”声明语句
1 | assign a = b & c **2) |
用实例元件
1 | and #2 u1(q, a, b) |
always块定义
1 | always @ (posedge clk or posedge clr) |
assign语句是描述组合逻辑最常用的方法之一。对assign之后不能加块,实现组合逻辑只能用逐句的使用assign。always块既可以用于描述组合逻辑,也可描述时序逻辑。always 块可用很多种描述手段来表达逻辑。按照一定的风格来编写always块,可以通过综合工具把源代码自动综合成用门级结构表示的组合或时序逻辑电路。
数据类型
与其他语言相同,在Verilog中数据类型也可以分为常量与变量两大类。下面是一些常用的数据类型。
常量
数字
4种进制表示形式(二、八、十、十六进制);x和z值在数字电路中的分别表示不定值和高阻值;定义负数时,减号写在数字定义表达式最前面,以补数形式表示;下划线可以用来分割数的表达以提高程序可读性。
1 | 8'b10101100 //<位宽><进制><数字> |
parameter型
使用parameter来定义一个常量标识符,可以提高程序的可读性和可维护性。parameter型数据和C语言中使用const修饰的变量很相像,不过在模块或实例引用时,可通过参数传递改变被引用模块或实例中已定义的参数。同样,可以使用defparam命令,在一个模块中改变另一个模块的参数。
1 | parameter msb = 7; |
变量
在程序运行过程中其值可以改变的量。
网络数据类型:表示结构实体(如:门)之间的物理连接。
不能储存值,且必须受到驱动器(如:assign)的驱动。无驱动器连接的情况下其值为高阻值。
wire型与tri型。
wire
wire类型是一种常用的网络数据类型。网络数据类型表示结构实体(例如门)之间的物理连接,这种类型不能存储值,而且必须受到启动器(例如门或连续赋值语句,assign)的驱动。如果没有启动器的连接,该变量就是高阻值z。
Verilog程序模块中输入、输出信号类型默认时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以用作“assign”语句或实例元件的输出。
1 | wire [n-1: 0] name_1, name_2, ... , name_i; |
reg
存储器数据类型。通过赋值语句可以改变寄存器存储的值,其作用与改变触发器存储的值相当。默认值为不定值x。reg型数据常用来表示“always”模块内指定信号,常代表触发器。在“always”模块内被赋值的每一个信号都必须定义成reg型。
1 | reg [n-1: 0] name_1, name_2, ... , name_i; |
memory
通过reg 型变量建立数组来对存储器进行建模,可以描述RAM型存储器、ROM存储器和reg文件。
1 | reg [n-1: 0] memory_name[m-1: 0]; |
运算符
- 算术运算符(+,-,x,/,%)
- 赋值运算符(=,<=)
- 关系运算符(>,<,>=,<=,==,!=,===,!==)
- 逻辑运算符(&&,||,!)
- 条件运算符(?:)
- 位操作运算符(~,|,^,&,^~,<<,>>,{ },“缩减运算符“)
注意:
- 由于操作数中某些位可能为不定值x或高阻值x,使用“==”和“!=”的结果可能为不定值x。而使用“===”和“!==”运算符时,某些位的不定值x和高阻值z也进行比较,两个操作数完全一致,结果才是1,否是为0。“===”和“!==”常用于case表达式的判别,所以又称“case等式运算符”。
- 移位运算用0来填补移出的空位。
- 缩减运算符和拼接运算符是不同与C语言的语法现象。
优先级别
操作符 | 优先级 |
---|---|
! ~ | 高 |
* / % | |
+ - | |
<< >> | |
< <= > >= | |
== != === !== | |
& | |
^ ^~ | |
| | |
&& | |
|| | |
? : | 低 |
语句
赋值语句
Verilog语言中,信号有两种赋值方式,非阻塞赋值(b <= a;)和阻塞赋值(b = a;)。阻塞语句执行后,被赋值的变量值会马上更新,而采用非阻塞赋值时,当整个块结束后才会完成本次赋值操作。
条件语句
条件语句分为两种,一种为if-else语句,另一种是case语句。条件语句必须在过程块语句(initial和always语句引导的执行语句集合)中使用,除了这两种块语句引导的begin-end块中可以编写条件语句外,模块中其他地方都不能编写。
if-else的用法与C语言中非常相似,但是要注意在每个if和else后使用begin-end来进行执行语句的集合。否则可能会引起else与if匹配上的错误,导致难以预测的后果。
case语句是一种多路分支语句,通常用于微指令处理器的指令译码。case语句的所有表达式值位宽必须相等。
1 | always @(*) |
针对电路特性Verilog提供了case语句的其他两种形式,casez和casex。casez语句来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。所谓不必关心的情况,即在表达式进行比较时,不将该位的状态考虑在内。
循环语句
四种循环语句有forever语句、repeat语句、while语句、for语句四种。
forever循环语句常用语陈升周期性波形,用来作为仿真测试信号。它与always语句不通之处在于不能独立写在程序中,而必须写在initial块中。
1 | forever statement; |
repeat语句中可以使用表达式指定循环执行的次数。表达式通常为常量表达式。
1 | repeat (expression) statement; |
另外两个循环语句,while和for与在C语言中有相同的用法。上述中的statement都可以使用begin-end来组织多个语句。
块语句
语句块的作用是将多条语句合并成一组,使它们像一条语句那样。在Verilog中块语句分为两种,一种是顺序块(也称过程块,关键字begin-end),另一种为并行块(关键字begin-end)。
在过程块中,语句一条接一条按顺序执行,除了带有内嵌延迟控制的非阻塞赋值语句。如果语句中包括延迟或事件控制,那么延迟总是相对于前面那条语句执行完成的仿真时间的。而在并行块中,所有语句并发执行。语句执行的顺序有个字语句内延迟或事件控制决定,且延迟或事件控制是相对于块语句开始执行的时刻而言的。
块语句还具有嵌套块和命名块的特点。块的嵌套使得顺序块和并行块能够混合在一起使用。如果给块进行命名,则可以在命名块中声明局部变量,此变量可以通过层次名引用进行访问。此外,命令块可以被禁用,如停止其执行(disable block_name;
)。
结构说明语句
在Verilog语言中的任何过程模块都从属于一下4中结构的说明语句
- initial说明语句
- always说明语句
- task说明语句
- function说明语句
initial 和 always
一个程序中可以有多个initial和always过程块。每个initial和always说明语句在仿真的一开始同时立即执行。initial 块只执行一次,而always块可以不断地反复执行,直到仿真结束。always块执行与否要看它的处罚条件是否满足。
1 | initial |
task 和 function
task和function分别用于定义任务和函数,它们把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。
任务和函数主要有以下4个不同点
- 函数只能与主模块共用一个仿真事件单位,而任务可以定义自己的仿真时间单位。
- 函数不能启动任务,而任务能启动其他任务和函数。
- 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
- 函数返回一个值,而任务则不返回值。
函数在定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。
Verilog 中的函数是不能够进行递归调用的。某函数在两个不同的地方被同时并发调用,会对同一块地址空间进行操作。如果使用了 关键字 automatic,则该函数将称为自动的或可递归的。
生成语句
生成语句(关键字 generate - endgenerate)可以动态地生成verilog代码,方便了参数化模块的生成。生成块的本质是使用循环内的一条语句来代替多条重复的verilog语句,简化用户的编程。
在Verilog中有3种创建生成语句的方法分别为,循环生成、条件生成和case生成。
1 | // generate example |
对生成语句的作用还不甚理解,日后工程中再去理解。
常用系统调用与预编译处理
常用系统调用
系统调用 | 说明 |
---|---|
$display 和 $write | 用来输出信息,类似于C语言中的printf |
$strobe | 选通显示 |
$fopen | 打开文件 |
$fdisplay $fmonitor $fwrite $fstrobe | 文件操作 |
$fclose | 关闭文件 |
%m | 显示层次 |
$monitor $monitoron $monitoroff | 提供了监控和输出参数列表中表达式或变量值的功能 |
$time $realtime | 时间度量系统函数 |
$finish | 退出仿真器,返回主操作系统,结束仿真过程 |
$stop | 把EDA工具(如仿真器)置成暂停模式 |
$readmemb 和 $readmemh | 从文件中读取数据到存储器中 |
$random | 随机数产生 |
$test$plusargs | 描述条件执行 |
预编译处理
宏定义 | 说明 |
---|---|
`define | 宏定义 |
`include | 文件包含 |
`timescale | 时间尺度 |
`ifdef | 条件编译命令 |
`else | 条件编译命令 |
`endif | 条件编译命令 |
参考书籍
夏宇闻. VERILOG数字系统设计教程, (第二版)[M]. 北京航空航天大学出版社, 2008.
Author: Hatton.Liu
Link: http://hattonl.github.io/2018/09/07/verilog-basic-grammar/
License: 知识共享署名-非商业性使用 4.0 国际许可协议