zl程序教程

您现在的位置是:首页 >  工具

当前栏目

Verilog HDL学习笔记

笔记学习 Verilog
2023-09-11 14:20:47 时间

1 硬件描述语言简介

1.1 概述

硬件描述语言HDL(Hardware Description Language)是一种形式化方法老描述数字电路和数字逻辑系统的语言。数字逻辑电路设计者可以利用这种语言来描述自己的设计思想。然后用EDA工具进行仿真,在自动综合到门级电路,最后用ASIC/FPGA来实现其功能。

1.2 HDL语言特点

  • HDL语言既包含一些高层次程序设计语言的结构形式,同时也兼顾描述硬件线路连接的具体构件。
  • 通过使用结构级或行为级描述,可以在不同的抽象层次(系统级、算法级、寄存器传输级、逻辑级、电路级)描述设计,支持自顶向下的数字电路设计方法。
  • HDL语言是并发的,即具有在同一时刻执行多任务的能力。一般来讲编程语言(例如C语言)是顺序执行的,但在实际硬件中许多操作都是在同一时刻发生的,因此HDL语言具有并发的特征。
  • HDL语言有时序的概念。一般来讲编程语言没有时序的概念的,但在硬件电路中,从输入到输出总是有延迟存在的,为此HDL语言需要引入时序的概念,提供描述电路时序的要求能力。

2 程序的基本语法

2.1 Verilog HDL 程序结构

Verilog HDL程序由模块(Module)组成,类似于C语言程序由函数组成。模块用于描述某个设计的功能和结构,及其与其他模块通讯(连接)的外部端口。在Verilog HDL语言中,一个电路就是一个Module。

2.1 Verilog HDL 程序规则

  • 程序严格区分大小写,关键字只能用小写。逗号、分号、引号。圆括号等均使用半角应为符号。

  • 除了endmodule语句(以及编译器指令)外,每个语句以分号结束。

  • 一行可以写多个语句,一个语句也可以分写多行。

  • Verilog HDL 采用 begin、end 来围住顺序语句块(且begin、end后面也没有分号)而{ }则被 Verilog HDL另用作拼接符。

  • Verilog HDL 定义了一些列的关键保留字,关键字都是小写,在编程时,注意给变量或者标识符命名不同于关键字。

模块

连续赋值语句assign

过程块initial和always

3 Verilog HDL 基本语法

3.1 Verilog HDL 基本语言要素

3.1.1 空白符

Verilog HDL 空白符包括:空格(\b)、制表符(\t)、换行符(\n)。如果空白符不出现在字符串中,则空白符被忽略,空白符除了器分割的作用外,还可以在必要的地方插入相应的空白符,一方便读者阅读和修改。

笔记:刚开始学习Verilog HDL程序时,以为程序编程方式和Python一样是段落式结构,以此来区分不同的包含关系。现在看来,仅仅是为了方便阅读,但是编程过程中,仍然要保留这种编写方式。

3.1.2 注释符

单行注释://
多行注释:/* … */

笔记:Verilog HDL 中的注释用法和C语言中的注释用法相同。

3.1.3 关键字

和一些软件编程语言相似,Verilog HDL也有自己的关键字,又称保留字,用户不可以卵用。所有的关键字都使用小写字母。

常见的关键字

关键字释义
always

3.1.4 标识符

标识符是程序代码中对象的名字,设计人员使用标识符来访问对象。Verilog HDL 标识符可以是任意的字母、数字、下划线和$ 的组合。但是标识符第一个字符必须是字母或下划线。标识符是区分大小写的。

3.2 常量

3.2.1 四种基本逻辑数值

0逻辑零
1高电平
x未知逻辑
z高阻态

3.2.2 整形

  • 全面描述格式:<位宽>'<进制><数字>
  • 默认位宽,具体由系统决定,至少32位:'<进制><数字>
  • 默认进制,采用十进制:<数字>
例子解释
4’b1001位宽4位二进制数1001
5’o36位宽5位八进制数36
3’d98位宽3位十进制数98
2’hb2位宽2位十六进制数b1

注:进制的字母和“ ’ ”之间不能有空格。在数字串中间可以任意加入下划线,提高长数字的易读性。

3.2.3 实数

  • 十进制数表示方式:3.14
  • 科学计数法表示方式:1.5E2 (表示数字1.5×102=150)

Verilog HDL定义了实数如何隐式转换为整数。实数通过四舍五入转换为相近的整数。(例如:12.4→12;90.5→91)

3.2.4 字符串

字符串是双引号内的字符序列,不能分行写,例如:“ASDF”。一个字符可以看做其ASCLL编码对应的8为无符号整数。因而4个字符组成的字符串"ABCD"可以用位宽为8×7的变量来存储,字符串变量属于reg型变量。

3.2.5 参数

参数是一个被命名的常量,称为符号常量。在Verilog HDL中可用parameter定义参数。

格式:parameter 参数名1 = 表达式,参数名2 = 表达式,…,参数名n = 表达式;
例如:parameter S0 = 2'b01 , s1 = 2'b02 , s3 = 2'b03;

定义位置:

  • 模块内部
  • 模块开头
  • 端口列表之前

3.3 变量及其数据类型

3.3.1 wire型(线网型)

wire型变量常用来表示以assign关键字指定的组合逻辑信号。
程序的输入/输出信号类型默认为wire型。
对综合器来说,wire型变量取值可为0,1,x,z,如果没有连接到驱动源,则默认初始值是高阻抗z。
wire型信号可以用作任何方程式的输入,也可做assign语句或实例元件的输出。

声明格式

wire [n-1:0] 变量名1,变量名2,...变量名i; //共有i条总线,每条总线内有n条线路。
/*其中[n-1:0]也可以写成[n:1],代表该变量的位宽有多少位,若省略方括号部分,
则表示位宽为1位。一次定义多个变量是,变量名之间用逗号分隔开,语句最后用分号结束。*/

//例如
wire a,b;   //定义2个1位的wire型变量a和b
wire [7:0] c; //定义1个8为的wire型变量

线网型变量的赋值(也就是驱动)不能在always块内对其赋值,只能通过数据流assign操作来完成。

wire [3:0] w1; //定义1个8为的wire型变量
assign w1 = 4'b1011; //要用assign来赋值,此句不能出现在always块内

在端口说明中被声明为 input,inout 型的端口只能被定义为线网型变量,被声明为 output 型的端口可定义为线网型或寄存器型。如果不加定义,默认为线网型。

3.3.2 reg型(寄存器型)

reg型变量是最常用的 variable 型变量。
寄存器是存储数据存储单元的抽象。
reg 型变量并不意味着一定对应着硬件上的触发器和寄存器,而是根据具体情况来确定其映射为寄存器或连线。
在always块内被赋值的每一个信号都必须定义为 reg 型,而 reg 行也暗示了被定义的信号将用在 always 块内。

reg a;// 定义了1个1位的reg变量
reg [3:0] regb,regc; // 定义了2个4位的reg变量。

3.4 运算符

3.4.1 算数运算符

符号解释
+
-
*
/
%取余
**乘方

3.4.2 关系运算符

符号解释
>大于
<小于
>=大于等于
<=小于等于
==逻辑相等
!=逻辑不相等
===全等
!===非全等

3.4.3 逻辑运算符

符号解释
&&逻辑与
||逻辑或
!逻辑非

3.4.4 位运算符

符号解释
&按位与
|按位或
~按位非
^按位异或

3.4.5 条件运算符

x ? a : b

/*通过判断条件表达式x的真假,从2个表达式中选1个作为输出结果。 */
伪代码:
变量 = 条件表达式 ? (表达式1):(表达式2/*   如果表达式结果为真,则输出表达式1,否则输出表达式2   */

//if写法
if(条件表达式)
{
	;//表达式1
}
else
{
	;//表达式2
}


3.4.6 位拼接运算符

位拼接运算符可以将两个或更多信号的某些位拼接起来进行运算操作。
{信号1的某几位,信号2的某几位,…,信号n的某几位}

4 Verilog HDL 基本语句

4.1 选择语句

4.1.1 if 语句

if(条件表达式)
	语句1;
else
	语句2;

if(条件表达式1)
	语句1;
else if(条件表达式2)
	语句2;
else if(条件表达式3)
	语句3;
else
	语句4;

“语句1”和“语句2”都可以换成用begin和end之间的语句块。
“语句1”和“语句2”如果都是 if 语句,其 else 语句可有可无,这样会形成嵌套,看起来比较复杂,甚至会产生歧义。

4.1.1 case 语句

case(控制表达式)
	分支表达式1:  执行语句1;
	分支表达式2:  执行语句2;
	...
	分支表达式n-1:  执行语句n-1;
	default :     执行语句n;
endcase

case语句和C语言中的switch语句相似,且功能相同,但是用法不同。

4.2 重复语句

4.2.1 for 循环语句

for(i=0;i<6;i++)
begin
	循环语句1;
	循环语句2;
end

4.2.2 while 循环语句

while(i<3) begin
	$ display("Hello word, %d",i);
	i = i + 1;
end

4.2.3 repeat 循环语句

repeat(循环次数) 过程语句;

  • 这种循环语句执行指定的循环次数的过程语句。
  • 如果循环次数表达式的值不确定,即为x或z时,那么循环次数按0处理。
repeat(ShifBy) p_reg = p_reg << 1; // 将p_reg左移ShifBy位

4.2.4 forever 循环语句

  • orever 必须写在 initial 过程块中;
  • 用法和 repeat 循环语句相同;

4.3 任务和函数语句

4.3.1 task 任务

定义格式:

task 任务名
	输入和输出声明;  // 声明的顺序很重要,决定了被调用时传递给任务的变量顺序
	局部变量声明;
	begin
	...
	end
endtask

定义格式:

4.3.2 function 函数

function <返回值的类型或者函数声明> 函数名
	输入声明;  // 声明的顺序很重要,决定了被调用时传递给任务的变量顺序
	局部变量声明;
	begin
	...
	end
endfunction 

5 Verilog HDL 数据流建模

5.1 连续赋值语句 assign

用数据流描述方式建模,最基本的机制就是使用连续赋值语句 assign 语句。在连续赋值语句中,某个值被指派给线网型变量。

连续赋值语句的主要特点如下:

  • 连续赋值语句等号左边的变量必须是线网型(wire型)的,不能是寄存器型(reg型)的。寄存器型变量的赋值只能是在过程块(initial / always)中进行。
  • assign 不能出现在过程块(initial / always)中。
  • 等号右边的表达式中,操作数可以是线网型变量或寄存器型变量。
  • 连续赋值语句总是处于激活状态。只要等号右边的表达式中任意一个操作数发生变化,表达式就会立即重新计算,并检结果赋给等号左边的线网型变量。
  • 连续赋值语句是并发执行的,多个 assign 语句之间的执行次序与出现次序无关。

6 宏定义写法

`define		W_IDLE		    4'd0                // 定义常量
`define	  end_trp			cnt_clk	== TRP_CLK // 定义表达式 

7 文件包含写法

`include "sdram_para.v"	

//调用里面的定义,end_trsc  I_DONE  I_TRSC 均为里面的宏定义
init_state <= (`end_trsc) ? `I_DONE : `I_TRSC;

未完待续…

引用:
【1】 刘昌华,班鹏新,周劲.数字逻辑原理与FPGA设计[M]. 北京:北京航空航天大学出版社,2021.1