VLSI设计 - Verilog入门



Verilog是一种硬件描述语言(HDL)。它是一种用于描述数字系统(如网络交换机、微处理器、存储器或触发器)的语言。这意味着,使用HDL,我们可以描述任何级别的数字硬件。用HDL描述的设计与技术无关,设计和调试非常容易,通常比原理图更有用,尤其对于大型电路。

Verilog支持多个抽象级别的设计。主要的三个级别是:

  • 行为级
  • 寄存器传输级
  • 门级

行为级

此级别使用并发算法(行为级)来描述系统。每个算法都是顺序的,这意味着它由一系列逐一执行的指令组成。函数、任务和块是主要元素。不考虑设计的结构实现。

寄存器传输级

使用寄存器传输级的设计使用操作和寄存器之间的数据传输来指定电路的特性。现代RTL代码的定义是“任何可综合的代码都称为RTL代码”。

门级

在逻辑级别内,系统的特性由逻辑链接及其时序特性描述。所有信号都是离散信号。它们只能具有确定的逻辑值(`0`,`1`,`X`,`Z`)。可用的操作是预定义的逻辑原语(基本门)。门级建模可能不是逻辑设计的正确方法。门级代码是使用综合工具生成的,其网表用于门级仿真和后端。

词法标记

Verilog语言源文本文件是词法标记流。一个标记由一个或多个字符组成,每个字符都精确地在一个标记中。

Verilog HDL使用的基本词法标记类似于C编程语言中的标记。Verilog区分大小写。所有关键字都小写。

空白符

空白符可以包含空格、制表符、换行符和换页符。除非这些字符用于分隔标记,否则它们会被忽略。

空白符字符包括空格、制表符、回车符、换行符和换页符。

注释

有两种形式表示注释:

  • 1) 单行注释以标记 // 开始,以回车符结束。

例://这是单行语法

  • 2) 多行注释以标记 /* 开始,以标记 */ 结束。

例:/* 这是多行语法 */

数字

您可以以二进制、八进制、十进制或十六进制格式指定数字。负数以二进制补码表示。Verilog允许整数、实数以及有符号和无符号数。

语法如下:<size> <radix> <value>

可以在 <Size> 中定义大小或无大小数字,<radix> 定义它是二进制、八进制、十六进制还是十进制。

标识符

标识符是用于定义对象(例如函数、模块或寄存器)的名称。标识符应以字母字符或下划线字符开头。例:A_Z,a_z,_

标识符是字母、数字、下划线和 $ 字符的组合。它们最多可以包含 1024 个字符。

运算符

运算符是用于设置条件或操作变量的特殊字符。有时会使用一个、两个或三个字符来对变量执行操作。

例:>,+,~,&!=。

Verilog关键字

在Verilog中具有特殊含义的词称为Verilog关键字。例如,assign、case、while、wire、reg、and、or、nand和module。它们不应用作标识符。Verilog关键字还包括编译器指令以及系统任务和函数。

门级建模

Verilog具有内置的原语,如逻辑门、传输门和开关。这些很少用于设计工作,但在综合后用于ASIC/FPGA单元建模。

门级建模具有两个特性:

驱动强度 - 输出门的强度由驱动强度定义。如果与源直接连接,则输出最强。如果连接是通过导通晶体管,则强度降低;如果通过上拉/下拉电阻连接,则强度最低。通常不指定驱动强度,在这种情况下,强度默认为 strong1 和 strong0。

延迟 - 如果未指定延迟,则门没有传播延迟;如果指定两个延迟,则第一个表示上升延迟,第二个表示下降延迟;如果只指定一个延迟,则上升和下降延迟相等。在综合中可以忽略延迟。

门原语

Verilog中使用具有一个输出和多个输入的基本逻辑门。GATE 使用 and、nand、or、nor、xor、xnor 这些关键字中的一个,用于 Verilog 中 N 个输入和 1 个输出。

Example:  
   Module gate() 
   Wire ot0; 
   Wire ot1; 
   Wire ot2; 
   
   Reg in0,in1,in2,in3; 
   Not U1(ot0,in0); 
   Xor U2(ot1,in1,in2,in3); 
   And U3(ot2, in2,in3,in0) 

传输门原语

传输门原语包括缓冲器和反相器。它们具有单个输入和一个或多个输出。在下方的门实例化语法中,GATE 代表关键字 buf 或 NOT 门。

示例:Not,buf,bufif0,bufif1,notif0,notif1

Not – n 输出反相器

Buf – n 输出缓冲器

Bufifo – 三态缓冲器,低电平使能

Bufif1 – 三态缓冲器,高电平使能

Notifo – 三态反相器,低电平使能

Notif1 – 三态反相器,高电平使能

Example:  
   Module gate() 
   Wire out0; 
   Wire out1; 
   
   Reg in0,in1;
   Not U1(out0,in0); 
   Buf U2(out0,in0); 

数据类型

值集

Verilog 主要由四个基本值组成。Verilog 中使用到的所有 Verilog 数据类型都存储这些值:

0(逻辑零,或假条件)

1(逻辑一,或真条件)

x(未知逻辑值)

z(高阻态)

x 和 z 的使用在综合中非常有限。

线网 (Wire)

线网用于表示电路中的物理线,并用于连接门或模块。线网的值只能读取,不能在函数或块中赋值。线网不能存储值,但始终由连续赋值语句或将线网连接到门/模块的输出驱动。其他特定类型的线网包括:

Wand(线与) - Wand 的值取决于连接到它的所有设备驱动器的逻辑与。

Wor(线或) - Wor 的值取决于连接到它的所有设备驱动器的逻辑或。

Tri(三态) - 连接到 tri 的所有驱动器必须为 z,只有一个例外(它决定 tri 的值)。

Example: 
   Wire [msb:lsb] wire_variable_list; 
   Wirec // simple wire 
   Wand d; 
   
   Assign d = a; // value of d is the logical AND of 
   Assign d = b; // a and b 
   Wire [9:0] A; // a cable (vector) of 10 wires. 
   
   Wand [msb:lsb] wand_variable_list; 
   Wor [msb:lsb] wor_variable_list; 
   Tri [msb:lsb] tri_variable_list; 

寄存器 (Register)

reg(寄存器)是一个数据对象,它保存从一个过程赋值到下一个过程赋值的值,并且仅用于不同的函数和过程块。reg 是一个简单的 Verilog 变量类型寄存器,不能暗示物理寄存器。在多位寄存器中,数据以无符号数的形式存储,并且不使用符号扩展。

示例:

reg c; // 单个 1 位寄存器变量

reg [5:0] gem; // 一个 6 位向量;

reg [6:0] d, e; // 两个 7 位变量

输入 (Input)、输出 (Output)、双向 (Inout)

这些关键字用于声明任务或模块的输入、输出和双向端口。这里的输入和 inout 端口是线网类型,输出端口配置为线网、寄存器、wand、wor 或 tri 类型。默认始终为线网类型。

示例

Module sample(a, c, b, d);  
Input c;   // An input where wire is used. 

Output a, b;  // Two outputs where wire is used. 
Output [2:0] d;  /* A three-bit output. One must declare type in a separate statement. */ 
reg [1:0] a;  // The above ‘a’ port is for declaration in reg.

整数 (Integer)

整数用于通用变量。它们主要用于循环索引、常量和参数。它们是“reg”类型的数据类型。它们将数据存储为有符号数,而显式声明的 reg 类型将它们存储为无符号数据。如果在编译时未定义整数,则默认大小为 32 位。

如果整数保存一个常量,则综合器会在编译时将其调整为所需的最小宽度。

示例

Integer c;   // single 32-bit integer 
Assign a = 63;  // 63 defaults to a 7-bit variable. 

Supply0,Supply1

Supply0 定义连接到逻辑 0(接地)的线网,Supply1 定义连接到逻辑 1(电源)的线网。

示例

supply0 logic_0_wires; 
supply0 gnd1;  // equivalent to a wire assigned as 0 

supply1 logic_1_wires; 
supply1 c, s;

时间 (Time)

时间是一个 64 位量,可以与 $time 系统任务一起使用以保存仿真时间。时间不支持综合,因此仅用于仿真目的。

示例

time time_variable_list; 
time c; 
c = $time;   //c = current simulation time

参数 (Parameter)

参数定义一个常量,可以在使用模块时设置,允许在实例化过程中自定义模块。

Example 
Parameter add = 3’b010, sub = 2’b11; 
Parameter n = 3; 
Parameter [2:0] param2 = 3’b110; 

reg [n-1:0] jam; /* A 3-bit register with length of n or above. */ 
always @(z) 
y = {{(add - sub){z}};  

if (z)
begin 
   state = param2[1];
else
   state = param2[2]; 
end 

运算符

算术运算符

这些运算符执行算术运算。+ 和 - 用作一元 (x) 或二元 (z-y) 运算符。

包含在算术运算中的运算符为:

+(加法)、-(减法)、*(乘法)、/(除法)、%(模)

示例

parameter v = 5;
reg[3:0] b, d, h, i, count; 
h = b + d; 
i = d - v; 
cnt = (cnt +1)%16; //Can count 0 thru 15.

关系运算符

这些运算符比较两个操作数,并将结果返回为一位,1 或 0。

线网和寄存器变量为正。因此,(-3'd001) == 3'd111 和 (-3b001) > 3b110。

包含在关系运算中的运算符为:

  • ==(等于)
  • !=(不等于)
  • >(大于)
  • >=(大于或等于)
  • <(小于)
  • <=(小于或等于)

示例

if (z = = y) c = 1; 
   else c = 0; // Compare in 2’s compliment; d>b 
reg [3:0] d,b; 

if (d[3]= = b[3]) d[2:0] > b[2:0]; 
   else b[3]; 
Equivalent Statement 
e = (z == y);

按位运算符

按位运算符对两个操作数进行逐位比较。

包含在按位运算中的运算符为:

  • &(按位与)
  • |(按位或)
  • ~(按位非)
  • ^(按位异或)
  • ~^ 或 ^~(按位异或非)

示例

module and2 (d, b, c); 
input [1:0] d, b; 
output [1:0] c; 
assign c = d & b; 
end module 

逻辑运算符

逻辑运算符是按位运算符,仅用于单比特操作数。它们返回一位值,0 或 1。它们可以作用于整数或位组、表达式,并将所有非零值视为 1。逻辑运算符通常用于条件语句,因为它们作用于表达式。

包含在逻辑运算中的运算符为:

  • !(逻辑非)
  • &&(逻辑与)
  • ||(逻辑或)

示例

wire[7:0] a, b, c; // a, b and c are multibit variables. 
reg x; 

if ((a == b) && (c)) x = 1; //x = 1 if a equals b, and c is nonzero. 
   else x = !a; // x =0 if a is anything but zero.

归约运算符

归约运算符是按位运算符的一元形式,作用于操作数向量的所有位。这些也返回一位值。

包含在归约运算中的运算符为:

  • &(归约与)
  • |(归约或)
  • ~&(归约与非)
  • ~|(归约或非)
  • ^(归约异或)
  • ~^ 或 ^~(归约异或非)

示例

Module chk_zero (x, z); 

Input [2:0] x; 
Output z; 
Assign z = & x; // Reduction AND 
End module

移位运算符

移位运算符根据语法中第二个操作数指定的位数,对第一个操作数进行移位。对于左移和右移,空位都填充零(不使用符号扩展)。

包含在移位运算中的运算符为:

  • <<(左移)
  • >>(右移)

示例

Assign z = c << 3; /* z = c shifted left 3 bits;

空位填充 0 */

连接运算符

连接运算符将两个或多个操作数组合成一个更大的向量。

包含在连接运算中的运算符为:{ }(连接)

示例

wire [1:0] a, h; wire [2:0] x; wire [3;0] y, Z; 
assign x = {1’b0, a}; // x[2] = 0, x[1] = a[1], x[0] = a[0] 
assign b = {a, h}; /* b[3] = a[1], b[2] = a[0], b[1] = h[1], 
b[0] = h[0] */ 
assign {cout, b} = x + Z; // Concatenation of a result 

复制运算符

复制运算符用于创建项目的多个副本。

在复制运算中使用的运算符为:{n{item}}(项目的 n 倍复制)

示例

Wire [1:0] a, f; wire [4:0] x; 
Assign x = {2{1’f0}, a}; // Equivalent to x = {0,0,a } 
Assign y = {2{a}, 3{f}}; //Equivalent to y = {a,a,f,f} 
For synthesis, Synopsis did not like a zero replication.

For example:- 
Parameter l = 5, k = 5; 
Assign x = {(l-k){a}}

条件运算符

条件运算符综合成一个多路选择器。它与C/C++中使用的类型相同,根据条件计算两个表达式中的一个。

条件运算中使用的运算符为:

(条件) ? (条件为真时的结果) :

(条件为假时的结果)

示例

Assign x = (g) ? a : b; 
Assign x = (inc = = 2) ? x+1 : x-1; 
/* if (inc), x = x+1, else x = x-1 */ 

操作数

字面量

字面量是在Verilog表达式中使用的常量值操作数。两种常用的Verilog字面量是:

  • 字符串 - 字符串字面量操作数是一个字符的一维数组,用双引号 (" ") 括起来。

  • 数值 - 常量数值操作数以二进制、八进制、十进制或十六进制表示。

示例

n - 表示位数的整数

F - 四种可能的基数格式之一:

b表示二进制,o表示八进制,d表示十进制,h表示十六进制。

“time is”  // string literal 
267        // 32-bit decimal number 
2’b01      // 2-bit binary 
20’hB36F   // 20-bit hexadecimal number 
‘062       // 32-bit octal number 

线网、寄存器和参数

线网、寄存器和参数是Verilog表达式中用作操作数的数据类型。

位选择“x[2]”和部分选择“x[4:2]”

位选择和部分选择分别用于使用方括号“[ ]”从线网、寄存器或参数向量中选择一位和多位。位选择和部分选择也像它们的主要数据对象一样用作表达式中的操作数。

示例

reg [7:0] x, y; 
reg [3:0] z; 
reg a; 
a = x[7] & y[7];      // bit-selects 
z = x[7:4] + y[3:0];  // part-selects 

函数调用

在函数调用中,函数的返回值直接用于表达式,无需先将其赋值给寄存器或线网。只需将函数调用作为一种操作数类型即可。需要确保知道函数调用的返回值的位宽。

Example  
Assign x = y & z & chk_yz(z, y); // chk_yz is a function 

. . ./* Definition of the function */ 
Function chk_yz; // function definition 
Input z,y; 
chk_yz = y^z; 
End function 

模块

模块声明

在Verilog中,模块是主要的电路设计单元。它指明模块名称和端口列表(参数)。接下来的几行指定每个端口的输入/输出类型(input、output或inout)和位宽。默认端口位宽为1位。端口变量必须用wire、wand…、reg声明。默认端口变量为wire。通常,输入为wire,因为它们的数据在模块外部锁存。如果输出信号存储在模块内部,则输出为reg类型。

示例

module sub_add(add, in1, in2, out); 
input add; // defaults to wire 
input [7:0] in1, in2; wire in1, in2; 

output [7:0] out; reg out; 
... statements ... 
End module 

连续赋值

模块中的连续赋值用于将值赋给线网,这是在always或initial块外部使用的常规赋值。此赋值使用显式的assign语句完成,或者在声明线网时为其赋值。连续赋值在仿真期间连续执行。赋值语句的顺序不影响它。如果任何右侧输入信号发生变化,它将改变左侧输出信号。

示例

Wire [1:0] x = 2’y01;   // assigned on declaration 
Assign y = c | d;       // using assign statement 
Assign d = a & b; 
/* the order of the assign statements does not matter. */ 

模块例化

模块声明是创建实际对象的模板。模块在其他模块内部例化,每个例化都从该模板创建一个单个对象。例外情况是顶层模块,它本身就是一个例化。模块的端口必须与模板中定义的端口匹配。它被指定为:

  • 按名称,使用点“ .模板端口名 (连接到端口的线网名称)” 。或者

  • 按位置,将端口放在模板和实例的端口列表中的相同位置。

示例

MODULE DEFINITION 
Module and4 (x, y, z); 
Input [3:0] x, y; 
Output [3:0] z; 
Assign z = x | y; 
End module 
广告