VLSI设计-Verilog简介


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

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

  • Behave水平
  • 寄存器传输层
  • 门级

Behave水平

该级别通过并发算法(Behave)描述系统。每个算法都是顺序的,这意味着它由一组逐条执行的指令组成。功能、任务和块是主要元素。不考虑设计的结构实现。

寄存器-传输级别

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

门级

在逻辑层中,系统的特征由逻辑链路及其时序属性来描述。所有信号都是离散信号。它们只能有明确的逻辑值(“0”、“1”、“X”、“Z”)。可用的操作是预定义的逻辑原语(基本门)。门级建模可能不是逻辑设计的正确想法。门级代码是使用综合工具等工具生成的,其网表用于门级模拟和后端。

词汇标记

Verilog 语言源文本文件是词汇标记流。一个令牌由一个或多个字符组成,并且每个单个字符恰好位于一个令牌中。

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

空白

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

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

评论

评论有两种形式表示

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

例如: //这是单行语法

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

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

数字

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

语法由以下给出 - <size> <radix> <value>

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

身份标识

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

标识符是字母、数字、下划线和 $ 字符的组合。它们的长度最多为 1024 个字符。

运营商

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

前任。>、+、~、&!=。

Verilog 关键字

在 Verilog 中具有特殊含义的单词称为 Verilog 关键字。例如,分配、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 gateway。

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

非-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 在合成中的使用非常有限。

金属丝

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

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; 

登记

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

示例 -

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

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

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

输入、输出、输入输出

这些关键字用于声明任务或模块的输入、输出和双向端口。这里输入和输入输出端口为wire类型,输出端口配置为wire、reg、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.

整数

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

如果整数保持常量,合成器会将它们调整为编译时所需的最小宽度。

例子

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

供应0、供应1

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

例子

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

supply1 logic_1_wires; 
supply1 c, s;

时间

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

例子

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

范围

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

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)形式返回结果。

Wire 和 reg 变量为正。因此 (−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.

归约运算符

归约运算符是按位运算符的一元形式,对操作数向量的所有位进行运算。它们还返回一个单位值。

归约运算中包含的运算符是 -

  • &(减少与)
  • | (减少或)
  • ~&(还原NAND)
  • 〜| (还原或非)
  • ^(减少异或)
  • ~^ 或 ^~(异或非)

例子

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 文字是 -

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

  • 数字- 常数操作数以二进制、八进制、十进制或十六进制数指定。

例子

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 中,模块是主要的设计实体。这表示名称和端口列表(参数)。接下来的几行指定输入/输出类型(输入、输出或输入输出)和每个端口的宽度。默认端口宽度仅为 1 位。端口变量必须通过wire、wand、来声明。。.,注册。默认端口变量是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 块外部使用的正常赋值。此分配是通过显式分配语句完成的,或者是在声明期间将值分配给连线。连续赋值在仿真时连续执行。赋值语句的顺序不影响它。如果您对任何右侧输入信号进行任何更改,都会更改左侧输出信号。

例子

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. */ 

模块实例化

模块声明是用于创建实际对象的模板。模块在其他模块内实例化,并且每个实例化都从该模板创建单个对象。例外是顶层模块,它是它自己的实例化。模块的端口必须与模板中定义的端口相匹配。指定 -

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

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

例子

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