• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

Verilog永无止境

互联网 diligentman 4天前 8次浏览

Verilog语法简介

@author: 紫金港小李

@mail: 3557727504@qq.com

文章目录

  • Verilog语法简介
  • 一、门级建模
    • 1.基本定义
      • (1)模块定义
      • (2)端口声明
      • (3)门级调用
      • (4)模块实例化
  • 二、数据流级建模
    • 1. assign(类似于赋值语句)
      • (1) 最简单的例子莫过于导线了~~
      • (2) 与或非门
    • 2. 操作数
      • (1)数字
      • (2)参数
      • (3)线网
      • (4)寄存器
    • 3. 操作符
      • (1) 详解拼接
      • (2) 缩减操作符
    • 4. Vector (可以理解成数组)
      • Declaring Vectors(定义)
      • Implicit nets(如果变量未定义即出现,可能出bug
      • Accessing Vector Elements: Part-Select(数组寻址?)
  • 三、行为级建模
    • 1. initial结构
    • 2. always结构
      • (1)基于延迟的控制
      • (2)基于电平敏感的控制
      • (3)基于边沿敏感控制

一、门级建模

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) 最简单的例子莫过于导线了~~

Verilog永无止境

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;

做个题目放松放松?Verilog永无止境

`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.

(2) 缩减操作符

The reduction operators can do AND, OR, and XOR of the bits of a vector, producing one bit of output:
缩减操作符可以对一个vector执行 “与”,“或”,“异或” 等操作,产生一个位的输出。

& a[3:0]     // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0]     // OR:  b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0]     // XOR: c[2]^c[1]^c[0]

4. Vector (可以理解成数组)

  • Declaring Vectors(定义)

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.
  • Implicit nets(如果变量未定义即出现,可能出bug)

   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便于查找错误)

  • Accessing Vector Elements: Part-Select(数组寻址?)

   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.

三、行为级建模

1. initial结构

顾名思义,initial结构的主要功能就是进行初始化,是设计者进行信号和变量初始化的时候常用的形式。由其用途可推断其用法,initial结构仅在仿真开始的时候激活一次,然后该结构中所有语句被执行一次,执行结束后不再执行。

initial
begin
    a=0;b=0;
#15 a=1;b=0;
#15 a=0;b=1;
#15 a=1;b=1;
#15 a=0;b=0;
end

Verilog永无止境

2. always结构

不同于initial,always结构将一直执行到仿真结束。设计者如果想要可以执行多次的语句,就需要用到always结构。语句结构如下:

always <时序控制方式> 执行语句

always结构的控制方式有三种:

  • 基于延迟的控制
  • 基于电平敏感控制
  • 基于边沿敏感控制

(1)基于延迟的控制

使用#times的方式来控制语句

always #5 a=~a;

以下代码可以生成一个占空比为25%的时钟信号:

initial clock = 0;
always
begin
#15 clock = 1;
#5 clock = 0;
end

Verilog永无止境

(2)基于电平敏感的控制

基于事件的控制在实际建模中使用最多,其控制方式为“@”引导的事件列表,如下所示

always @ (敏感事件列表)

如果敏感事件有多个,可以使用“,”或or来分隔,这些事件之间是或的关系,只要满足一个即会触发,如下例:

always @ (a or b)
sum = a+b;

always @(c or d)
begin
	e = c&d;
    f = c|d;
end

有些时候,敏感事件过多,可以用“*”替代

always @ (*)

(3)基于边沿敏感控制

时序电路采用的敏感列表一般是边沿敏感的,信号的边沿用posedge(上升沿)和negedge(下降沿)来表示,如果出现多个事件依然可以用or或“,”间隔,但是不能用“*”来省略。

例如:上升沿触发的同步D触发器

always @ (posedge clock)
begin
    if(!reset)
        q=0;
    else
        q=d;
end

在学了,在学了。
Verilog永无止境


喜欢 (0)