Verilog语法简介
@author: 紫金港小李
@mail: [email protected]
文章目录
一、门级建模
1.基本定义
(1)模块定义
模块定义以关键字module开始,以关键字endmodule结束。基本语法结构如下:
module 模块名 (端口名1,端口名2,...);
...
endmodule
(2)端口声明
模块定义中的端口列表中仅仅列出了本模块具有哪些端口,但是并不标明输入or输出。这就需要在模块中进行声明:
output Y;
input A,B,C,D;
顾名思义,端口声明就是声明端口的类型,宽度等信息。类型有三种.
Verilog HDL 关键字 | 端口类型 |
---|---|
input | 输入端口 |
output | 输出端口 |
inout | 双向端口 |
Tips:
也可以端口声明放入模块定义中,举个栗子:
module top_module (
input in,
output out);
...
endmodule
(3)门级调用
端口声明后的部分就是门级调用,语法格式
逻辑门类型 <实例名称(可选)> (端口连接)
实例名称部分在门级建模一般不适用。逻辑门类型指的是常用的基本逻辑门,例如
not (S1n,S1);
not (En_n,En);
and (and1,En_n,S1n,S0n,A);
or (Y,and1,and2,and3);
门的名称 | 门的功能 |
---|---|
buf | 缓冲器 |
not | 非门 |
and | 与门 |
nand | 与非门 |
or | 或门 |
nor | 或非门 |
xor | 异或门 |
xnor | 同或门 |
(4)模块实例化
把当前模块(module)中调用其他模块来完成设计的过程称之为模块的实例化。语法结构如下:
模块名称 实例名称 (端口连接);
- 模块名称即已经定义好的其他模块的模块名
- 实例名称时在本模块内定义的新名称
- 端口连接是在当前模块中把实例化的模块所包含的端口进行连接,有两种连接方式
- 按顺序连接
- 按名称连接
下面是一个以16位全加器通过实例化得到32位全加器的例子:
module fadd32b(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire cout,x;
//add16,16 bits full adder
add16 inst1 (a[15:0],b[15:0],1'b0,sum[15:0],cout);
add16 inst2 (a[31:16],b[31:16],cout,sum[31:16],x);
endmodule
二、数据流级建模
1. assign(类似于赋值语句)
(1) 最简单的例子莫过于导线了~~
assign 线网信号名 = 运算表达式
module top_module (
input in,
output out);
assign out = in;//这里是连续赋值,意即在仿真结束前,out恒等于in
endmodule
(2) 与或非门
Verilog 有两种非,一种是bitwise-NOT (~
),另一种是logical-NOT (!
) , like C. 对于一位的运算,这两种并无区别。
module Inverter(
input in,
output out
);
assign out = ~in;//inverter gate
endmodule
Verilog有两种与,bitwise-AND (&
) and logical-AND (&&
) operators, like C. 对于一位的运算,这两种并无区别。
module AND(
input a,
input b,
output out );
assign out = a&b;//and gate
endmodule
同样,bitwise-OR (|
) and logical-OR (||
) operators, like C.
module OR(
input a,
input b,
output out );
assign out = a|b;//or gate
endmodule
2. 操作数
- 操作数有很多数据类型,如parameter,wire等19种,这里介绍常用的4种。
数据类型 | 功能 |
---|---|
parameter | 用于参数的描述 |
wire型 | 用于描述线网 |
reg型 | 用于描述寄存器 |
integer型 | 用于描述整数类型 |
(1)数字
在操作数中首先要介绍的是数字,数字的基本表示格式如下:
<位宽>'<进制><数值>
在这个格式中,数值是唯一不可缺少的。
- Verilog支持四种进制形式,二进制,八进制,十进制和十六进制,分别用B,O,D,H表示(不区分大小写)。
- 位宽表示一个数字到底包含几位信息,指明了数字的精确位数,这个位数是以它华为二进制数之后所具有的宽度来表示的。
还是一样的,举一个栗子。
2'b01 //二进制,相当于十进制:1
4'd11
6'o37 //八进制,相当于十进制:3*8+7=31
8'hab
- 位宽缺少或者进制缺少
'o37 //位宽采用默认宽度(一般32位),相当于00000000000000000000000000011111
37 //相当于十进制的37
(2)参数
有时候某些数字或字符需要多次使用,Verilog中可用关键字parameter来定义一个参数,用于指代某个常用的数值、字符串或表达式。
parameter 参数1 = 表达式1,参数2 = 表达式2 ;
在语法结构中,parameter是关键字,后面的参数名是设计者自己定义的一个标识符。
parameter size = 8;
parameter a=4,b=16;
parameter width = size-1;
parameter clock = a+b;
(3)线网
线网类型采用关键字wire来定义,用于描述模块中可能是连线的情况。其语法如下
wire [宽度申明] 线网名1,线网名2;
Example:
wire x;
wire [3:0] y;
wire [7:1] m,n;
(4)寄存器
在数字电路中有一类器件可以存储数据值,在Verilog中定义为reg类型
reg x;
reg [3:0] y;
reg [7:1] m,n;
除了表示一位或多位的寄存器,reg还可以用来定义存储器,语法如下:
reg [n-1:0] 存储器名称 [0:m-1];
reg [7:0] mema [0:255]; //这里定义了一个宽度为8位的存储器,共有256个存储单元
//每个单元用0~255的数字进行编号
mema[1] 就是指第一个的8位储存单元。
进行初始化时,初始化的过程中要对每个存储器单元进行初始化赋值,而不能采用整个存储器直接赋值的方法.
reg memb [0:255];
memb = 0;(错误)
memb[100] = 0;(正确)
整数是一种特殊的数据类型,使用关键字integer进行申明。
integer i;
i=-1;
做个题目放松放松?
`default_nettype none//这句的意思是,不允许未定义的变量参与运算,这与C语言类似,必须要先定义变量
module top_module(
input a,
input b,
input c,
input d,
output out,
output out_n );
wire and_1,and_2,or_1;//define three wire
assign and_1 = a&b;
assign and_2 = c&d;
assign or_1 = and_1|and_2;
assign out = or_1;
assign out_n = ~or_1;
endmodule
3. 操作符
操作类型 | 操作符 | 执行的操作 | 操作数的个数 |
---|---|---|---|
算术 | * / + - % ** |
乘 除 加 减 取余 求幂 |
2 |
按位 | ~ & | ^ ^~ |
按位求反 按位与 按位或 按位异或 按位同或 |
1 2 2 2 2 |
逻辑 | ! && || |
逻辑求反 逻辑与 逻辑或 |
1 2 2 |
关系 | > < >= <= |
大于 小于 大于等于 小于等于 |
2 |
等式 | == != === !== |
相等 不等 相等 不等 |
2 |
移位 | >> << >>> <<< |
右移 左移 算术右移 算术左移 |
2 |
拼接 | { } | 拼接 | 任意个数 |
缩减 | & ~& | |~ ^ ^~ |
缩减与 缩减与非 缩减或 缩减或非 缩减异或 缩减同或 |
1 |
条件 | ? : | 条件 | 3 |
(1) 详解拼接
拼接操作符可以完成几个信号的拼接,用大括号{}表示。使用如下:
reg [3:0] a,b,c;
a=4'b0000;
b=4'b1111;
c=4'b0101;
x1={a,b,c}; //0000_1111_0101
x2={a[3],b,c}; //0_1111_0101
x3={a[2:0],b[0],c}; //000_1_0101
x4={a[0],b[0],c[0],2'b00}; //01100
拼接符不仅可以处理上述问题,还可以重复某个信号 assign a = {b,b,b,b,b,b};
. 为了简化表达,语法如下:
{num{vector}}
Examples:
{
5{
1'b1}} // 5'b11111 (or 5'd31 or 5'h1f)
{
2{a,b,c}} // The same as {a,b,c,a,b,c}
{
3'd5, {2{3'd6}}} // 9'b101_110_110. It's a concatenation of 101 with
// the second vector, which is two copies of 3'b110.
4. Vector (可以理解成数组)
Vectors must be declared:
type [upper:lower] vector_name;
//for example:
wire [7:0] w; // 8-bit wire
reg [4:1] x; // 4-bit reg(register:寄存器变量)
output reg [0:0] y; // 1-bit reg that is also an output port (this is still a vector)
input wire [3:-2] z; // 6-bit wire input (negative ranges are allowed)
output [3:0] a; // 4-bit output wire. Type is 'wire' unless specified otherwise.
wire [0:7] b; // 8-bit wire where b[0] is the most-significant bit.
wire [2:0] a, c; // Two vectors
assign a = 3'b101; // a = 101
assign b = a; // b = 1 implicitly-created wire
assign c = b; // c = 001 <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared. // This could be a bug if the port was intended to be a vector.
Adding ``default_nettype none` would make the second line of code an error, which makes the bug more visible.(建议加上这条语句,使得直接报ERROR便于查找错误)
assign w = a;//a:4-bits,w:8-bits.
//the lower 4 bits in w is a ,while the higher 4 bits in zero.
//w的低位是a,高位为0.
takes the entire 4-bit vector a and assigns it to the entire 8-bit vector w (declarations are taken from above). If the lengths of the right and left sides don’t match, it is zero-extended or truncated as appropriate.
The part-select operator can be used to access a portion of a vector:
w[3:0] // Only the lower 4 bits of w
x[1] // The lowest bit of x
x[1:1] // ...also the lowest bit of x
z[-1:-2] // Two lowest bits of z,like python
b[3:0] // Illegal. Vector part-select must match the direction of the declaration.
b[0:3] // The *upper* 4 bits of b.
assign w[3:0] = b[0:3]; // Assign upper 4 bits of b to lower 4 bits of w. w[3]=b[0], w[2]=b[1], etc.
在学了,在学了。