1.1一个基本的Verilog代码构成

1
2
3
module 模块名 ([端口列表]);
[端口信号声明;]
[参数声明;]

1
2
3
4
5
6
7
8
9
module full_adder (A,B,CIN,S,COUT);
//端口列表是指电路的输入/输出信号名称列表,信号名由用户指定,各名称间用逗号隔开
input [3:0] A,B;
//端口信号声明是要说明端口信号的输入输出属性、信号的数据类型,以及信号的位宽
//输入输出属性有input,output,inout三种,信号的数据类型常用的有wire,reg两种
input CIN;
output reg [3:0] S;
//[3:0]代表信号的位宽,用[n1:n2]表示;同一类信号之间用逗号隔开
output COUNT;

1.2语法

(1)逻辑运算关系

算术型
***** 乘法
/ 除法
+ 加法
- 减法
% 求余
****** 求幂

其中运算规则为

  1. 加减乘除、求幂的操作数可以是实数也可以是整数,求余运算的操作数只能是整数
  2. 求余运算结果取第一个操作数的符号
逻辑型
逻辑非
&& 逻辑与
|| 逻辑或

其中运算规则为

  1. 逻辑型运算的结果可能是1(逻辑真)、0(逻辑假)、x(不确定)
  2. 逻辑运算的操作数可以是任意表达式,表达式的结果被当做逻辑值处理,只有1、0、x三种情况,非0、x即1
关系运算符
> 大于
< 小于
>= 大于等于
<= 小于等于
等价运算符
== 等于
= 不等于
=== **case ** 等于
== case 不等

其中运算规则为

  1. 等于和不等于运算的结果可能是1(逻辑真)、0(逻辑假)、x(不确定);对于x或a,认为是不确定的值,比较结果为x
  2. case等和case不等的结果只能是1或0,对于x、z认为是确定的值,参加比较
按位运算符

用于二进制数如1001与1111按照一位一位相比较

~ 按位非
& 按位与
| 按位或
^ 按位异或
~^ ^~ 按位同或

其中运算规则为

  1. 其它按位运算的操作数有2个或多个,将两个操作数对应的位两两运算
  2. 如果操作数位宽不同,位宽小的会自动左添0补齐
缩减运算符

缩减运算的操作数只有一个,将该数的各位自左至右进行逻辑运算,结果只有一位

& 缩减与
~& 缩减与非
| 缩减或
~| 缩减或非
^ 缩减异或
~^ ^~ 缩减同或
1
2
3
4
5
例:
Y=& 4’b1001;
//从左到右,逐位进行与运算,结果为0
Y= ~& 4’b1001;
//跟上面一样,先逐渐进行与运算,得出0后非
移位运算符

将一位或者多位向左或向右移n位

>> 右移
<< 左移
>>> 算术右移
<<< 算术左移
1
2
3
4
5
例:
Y= 4’b1001 >> 1;
// >> 1代表整体向右移动一位,空出来的部分自动补0,故结果为0100
Y= 4’sb1001 >>> 1;
// 算术整体右移一位,原来的最后一位1移动到最左边第一位,变成1100
拼接复制运算符

可以将两个四位二进制数链接起来

{} 拼接 {操作数1, 操作数2, …}
{ {} } 复制拼接 {n{ 操作数 1, 操作数2, …}}
1
2
3
4
5
例:
Y= {4’b1001, 2’b11};
//将二进制数1001和11拼接起来变成100111
Y= {4{2’b01}};
//前面的n为多少即复制拼接几次,这里n=4将01复制拼接4次,即01010101
条件运算符

跟C语言中一样,可以对未知数进行条件赋值

表达式1 ? 表达式2 : 表达式3 用于条件赋值
表达式1=1 结果等于表达式2
表达式1=0 结果等于表达式3
表达式1=x 结果为x

(2)assign语句

  • assign语句称作连续赋值语句,相当于数电中的电平触发,其总是处于激活状态,只要表达式中的操作数有变化,立即进行计算和赋值(与连续赋值语句对应的另一种语句称为过程赋值语句)
  • 赋值目标必须是wire型的,wire表示电路间的连线

这里给出一个assign语法的例子

1
2
3
assign y=a;
assign y=a&b;
//基本格式:assign 赋值目标 = 表达式

(3)always语句

  • always语句本身不是单一的有意义的一条语句,而是和下面的语句一起构成一个语句块,称之为过程块;过程块中的赋值语句称过程赋值语句
  • 该语句块不是总处于激活状态,当满足激活条件时才能被执行,否则被挂起,挂起时即使操作数有变化,也不执行赋值,赋值目标值保持不变
  • 赋值目标必须是reg型的
always语法的基本格式和例子
1
2
3
4
5
6
always @(敏感信号条件表)
//激活条件由敏感信号条件表决定,当敏感条件满足时,过程块被激活
各类顺序语句;
例:
always @(posedge CLK)
Q=D;

敏感条件即上面的@(敏感信号条件表)有两种,一种是边沿敏感,一种是电平敏感

边沿敏感
(posedge 信号名) 信号上升沿到来 例:(posedge clk)
(negedge 信号名) 信号下降沿到来 例:(negedge clk)
1
2
3
4
5
例:
always @ (posedge CLK)
Q=D;
//当CLK上升沿到来时,激活该语句块,将D的值赋给Q;否则,该语句块挂起,即使D有变化,Q的值也保持不变,直到下一次赋值
//就是数电中上升沿触发的D触发器
电平敏感
(信号名列表) 信号列表中任一信号有变化
例:(a,b,c) 的a,b,c中有一个信号发生改变,always下的过程块被触发

其中逗号也可以换成or就变成(a or b or c)

1
2
3
4
5
例:
always @ (D)
Q=D;
//当D有变化时(不管是由1变0还是由0变1),激活该语句块,将D的值赋给Q;否则,该语句块挂起,Q的值保持不变,直到下一次赋值
//实际是数电中的非门
在always语法中插入if和case等语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
例:
module DFF2 (CLK,D,Q,RST,EN)
input CLK,D,RST,EN;
//input定义时必须以字母为开头,数字开头不行
output Q;
reg Q;
//定义output若其与always后的代码块有关系,要将其定义为reg类型
always @(posedge CLK or negedge RST)
begin
if (!RST) Q<=0;
else if (EN) Q<=D
end
//使用if或者case语句的或者多个语句存在,要在代码块开头和结尾加上begin和end
endmodule

begin end之间的赋值语句有阻塞赋值和非阻塞赋值之分

阻塞赋值

语句顺序执行,前面的执行完才能执行后面,赋值符号:=右边表达式的计算和对左边寄存器变量的赋值是一个统一的原子操作中的两个动作,这两个动作之间不能再插入其他任何动作

1
2
3
4
5
6
7
8
9
赋值目标1=表达式1;
赋值目标2=表达式2;
//赋值语句1会阻塞赋值语句2 ,即只有当赋值语句1执行完才能执行赋值语句2
例:阻塞赋值
begin
m=a*b;
y=m;
end
//当m赋值完成后,才能执行y的赋值,y得到的是m的新值
非阻塞赋值

所有语句并行执行,赋值符号:<=首先按顺序计算右边表达式的值,但是并不马上赋值,而是要等到过程结束时再按顺序赋值。

1
2
3
4
5
6
7
8
9
赋值目标1<=表达式1;
赋值目标2<=表达式2;
//赋值语句1不会阻塞赋值语句2 ,赋值语句1和赋值语句2并行执行
例:非阻塞赋值
begin
m<=a*b;
y<=m;
end
//m和y的赋值并行执行,y得到的是m的旧值

底层模块和门语言调用

底层模块调用

为了实现多个门电路组合使用,我们先设计一个底层的基础的D触发器模块

其用代码表示为

1
2
3
4
5
6
module DFF(CLK,D,Q)
output reg Q;
input CLK,D;
always @ (posedge CLK)
Q<=D;
endmodule

这时我们再将两个模块组合起来,加入两个内部变量d1和q1调用底层模块,在调用底层模块的时候要遵顼这两个格式

  • 端口名关联法(命名法)
    因为有名字对应,不必按底层模块的端口信号列表顺序

    1
    2
    (.底层端口名1(外接信号名1),.底层端口名2(外接信号名2),…)
    DFF dff1(.CLK(clk),.D(d1),.Q(q1));
  • 位置关联法(顺序法)
    必须严格按照底层模块的端口信号列表顺序书写

    1
    2
    (外接信号名1,外接信号名2,…)
    DFF dff2(q1,d,q);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module examp (clk,d,a,q)
output q;
input clk,d,a;

wire d1;
wire q1;

DFF dff1(.CLK(clk),.D(d1),.Q(q1));
DFF dff2(q1,d,q);

or (d1,a,q);
//门原语或门

endmodule
门原语调用

Verilog语言提供已经设计好的门,称为门原语(primitive,共12个),这些门可直接调用,不用再对其进行功能描述

门原语调用格式:门原语名 实例名 (端口连接)

其中实例名可省略(和模块调用不同),端口连接只能采用顺序法,输出在前,输入在后。

于是实际情况:例:and (out, in1, in2);

端口连接中第一个是输出,其余是输入,输入个数不限

下面给出6个与门相关的门原语

and (与) or(或) xor(异或)
nand(与非) nor(或非) xnor(同或)
  • 特殊门
not (非门) not (OUT1, IN);
buf(缓冲器) buf b1_2out(OUT1, OUT2, IN);
三态门
bufif1(控制端1有效缓冲器) bufif1 b1 (out, in, ctrl);
bufif0(控制端0有效缓冲器) 如上修改名称
notif1(控制端1有效非门) notif1 n1 (out, in, ctrl);
notif0(控制端0有效非门) 如上修改名称

端口列表中前面是输出,中间是输入,最后是使能端,输出个数不限。

数字表示格式

无符号数的表示方法:<位宽>’ <进制><数字>