书※目:Verilog数字系统设计教程(第四版)夏宇闻 编著
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达)
应用工具:vcs 和 verdi
文章目录
一、运算符
- 运算符按功能分为9类:
算术运算符、逻辑运算符、关系运算符、等式运算符、缩减运算符、条件运算符、位运算符、移位运算符、位拼接运算符。 - 运算符按操作数的个数分为3类:
- 单目运算符–带一个操作数:逻辑非!、按位取反~、缩减运算符、移位运算符;
- 双目运算符–带两个操作数:算术、关系、等式运算符;逻辑、位运算符的大部分;
- 三目运算符–带三个操作数:条件运算符。
(1)算术运算符
算术运算符 | 说明 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 求模 |
- 进行整数除法运算时,结果值略去小数部分,只取整数部分!
- %称为求模(或求余)运算符,要求%两侧均为整型数据;
- 求模运算结果值的符号位取第一个操作数的符号位!【例 1】 -11%3,结果为-2
- 进行算术运算时,若某操作数为不定值x,则整个结果也为x。
【例 2】 除法和求模运算的区别
module arithmetic(
output reg [3:0] c,
output reg [3:0] d,
output reg [7:0] e,
input clk,
input [3:0] a,
input [3:0] b
);
always @ (posedge clk) begin
c <= a/b;
d <= a%b;
e <= a*b;
end
(2)逻辑运算符
逻辑运算符 | 说明 |
---|---|
&&(双目) | 逻辑与 |
▏▏(双目) | 逻辑或 |
!(单目) | 逻辑非 |
- 非零的操作数被认为是真(1‘b1);
- 零被认为是假(1‘b0);
- 不确定的操作数如4’bxx00, 被认为是不确定的(可能为零,也可能为非零)(记为1’bx); 但4’bxx11被认为是真(记为1’b1,因为它肯定是非零的)
真值表如下,一般为了提高代码的可读性,都会使用括号明确表达个运算符间的优先关系。
(a > b)&&(x > y),可以写成 a > b && x > y
注意进行逻辑运算后的结果为布尔值(为1或0或x),“&&”和“||”的优先级除高于条件运算符外,低于关系运算符、等式运算符等几乎所有运算符;逻辑非“!”优先级最高
(3)位运算符
- 位运算其结果与操作数位数相同。位运算符中的双目运算符要求对两个操作数的相应位逐位进行运算。
- 两个不同长度的操作数进行位运算时,将自动按右端对齐,位数少的操作数会在高位用0补齐。
【例 3】
若A = 5’b11001,B = 3’b101,
则A & B = (5’b11001)&(5’b00101)= 5’b00001
思考:&&运算符和&运算符的区别。
(4)关系运算符(双目)
关系运算符 | 说明 |
---|---|
< | 小于 |
<= | 小于或等于 |
> | 大于 |
>= | 大于或等于 |
- 运算结果为1位的逻辑值1或0或x。 关系运算时,若关系为真,则返回值为1;若声明的关系为假,则返回值为0;若某操作数为不定值x,则返回值为x;
- 所有的关系运算符优先级别相同;
- 关系运算符的优先级低于算术运算符。
a < size - 1 等同于: a<(size - 1)
size -(1<a) 不等同于: size-1<a
(5)等式运算符(双目)
等 式 运 算 符 | 说 明 |
---|---|
== | 等 于 |
!= | 不 等 于 |
=== | 全 等 |
!== | 不全等 |
- 运算结果为1位的逻辑值1或0或x;
- 等于运算符(= =)和全等运算符(= = =)的区别
使用等于运算符时,两个操作数必须逐位相等,结果才为1;若某些位为x或z,则结果为x。
使用全等运算符时,若两个操作数的相应位完全一致(如同是1,或同是0,或同是x,或同是z),则结果为1;否则为0- 所有的等式运算符优先级别相同。
【例 4】 真值表比较
if(A == 1'bx) $display("Aisx"); //当A等于x时,这个语句也不执行
if(A === 1'bx) $display("Aisx");//当A等于x时,这个语句执行
(6)缩减运算符
- 运算法则与位运算符类似,但运算过程不同!
- 对单个操作数进行递推运算,即先将操作数的最低位与第二位进行与、或、非运算,再将运算结果与第三位进行相同的运算,依次类推,直至最高位 。
- 运算结果缩减为1位二进制数
【例 5】
reg[3:0] a;
b=|a; //等效于 b =( (a[0] | a[1]) | a(2)) | a[3]
(7)移位运算符(单目)
用法:A>>n 或 A<<n,将操作数右移或左移n位,同时用n个0填补移出的空位。注意:左移位数会增加,右移位数不变
。
【例 6】
4'b1001>>3 = 4'b0001; 4'b1001>>4 = 4'b0000;
4'b1001<<1 = 5'b10010; 4'b1001<<2 = 6'b100100;
1<<6 = 32'b1000000
(8)条件运算符(三目)
格式:信号 = 条件?表达式1:表达式2。当条件为真,信号取表达式1的值;为假,则取表达式2的值。
(9)位拼接运算符
位拼接运算符为{ },用于将两个或多个信号的某些位拼接起来,表示一个整体信号。在位拼接表达式中不允许存在没有指明位数的信号。
【例 8】
output [3:0] sum; //和
output cout; // 进位输出
input[3:0] ina, inb;
input cin;
assign {
cout, sum} = ina + inb +cin;// 进位与和拼接在一起
{
a, b[3:0], w, 3’b101} = {
a, b[3], b[2], b[1], b[0], w, 1’b1, 1’b0, 1’b1 }
1、位拼接还可以使用重复法来简化表达式
如:{4{w}} //等同于{w,w,w,w}
2、 位拼接还可以使用嵌套的方法来表达
{b,3{a,b}} //等同于{b, a , b , a , b , a , b }
(10)算术运算符
可用重复法简化表达式,如:{4{w}} //等同于{w, w, w, w},还可用嵌套方式简化书写,如:{b, { 3{a, b} } } //等同于{b, {a, b}, {a, b}, {a, b}},也等同于{b, a, b, a, b, a, b}。
在位拼接表达式中,不允许存在没有指明位数的信号,必须指明信号的位数;若未指明,则默认为32位的二进制数!如{ {1,0} } = 64’h h00000001_ 000000000 ,注 意{ {1 ,0} } 不等于2’b1100。
(11)小结
二、关键词
Verilog HDL中有很多的关键字是事先定义好的确定符,用来组织语言结构。下面总结常用的关键字。
常用关键字 | 关键字描述 |
---|---|
module | 模块定义 |
input | 输入端口 |
output | 输出端口 |
inout | 双向端口 |
parameter | 符号常量定义 |
wire | wire型信号 |
reg | reg信号 |
always | 过程语句块 |
assign | 连续赋值语句 |
begin | 语句块的起始标志 |
end | 语句块的结束标志 |
posedge/negedge | 信号的上升沿和下降沿 |
case | case语句起始标记 |
default | case语句的默认分支标志 |
endcase | case语句结束 |
if | if/else语句 |
else | if/else语句 |
for | for循环语句标记 |
endmodule | 模块结束 |
关键字 | 关键字 | 关键字 | 关键字 | 关键字 |
---|---|---|---|---|
and | always | assign | begin | buf |
bufif0 | bufif1 | case | casex | casez |
cmos | deassign | default | defparam | disable |
edge | else | end | endcase | endfunction |
endprimitive | endmodule | endspecify | endtable | endtask |
event | for | force | forever | fork |
function | highz0 | highz1 | if | ifnone |
initial | inout | input | integer | join |
large | macromodule | medium | module | nand |
negedge | nor | not | notif0 | notif1 |
nmos | or | output | parameter | pmos |
posedge | primitive | pulldown | pullup | pull0 |
pull1 | rcmos | real | realtime | reg |
release | repeat | rnmos | rpmos | rtran |
rtranif0 | rtranif1 | scalared | small | specify |
specparam | strength | strong0 | strong1 | supply0 |
supply1 | table | task | tran | tranif0 |
tranif1 | time | tri | triand | trior |
trireg | tri0 | tri1 | vectored | wait |
wand | weak0 | weak1 | while | wire |
wor | xnor | xor |
三、赋值语句
表中只有4种语句不可综合(fork_join ,initial ,’include ,’timescale )。
(1)两类
- 连续赋值语句——assign语句,用于对wire型变量赋值,是描述组合逻辑最常用的方法之一
【例】 assign c = a&b; //a、b、c均为wire型变量 - 过程赋值语句——用于对reg型变量赋值,有两种方式:
非阻塞(non-blocking)赋值方式:赋值符号为<=,如 b <= a ;
阻塞(blocking)赋值方式:赋值符号为=,如 b = a ;
(2)非阻塞赋值和阻塞赋值的区别
- 非阻塞赋值:非阻塞赋值在块结束时才完成赋值操作,c的值比b落后一个时钟周期。
always @(posedge clk)begin
b <= a;
c <= b;
end
- 阻塞赋值:阻塞赋值在每一条语句结束时即完成赋值操作。在一个语句块中,如果有多条阻塞赋值语句,在前面的赋值语句还没有完成之前,后面的语句就不能被执行,就像被阻塞一样,因此称为阻塞赋值方式,这里的b和c的值一样。
always @(posedge clk)begin
b = a;
c = b;
end
四、结构说明语句
用来将两条或多条语句组合在一起,使其在格式上更像一条语句,以增加程序的可读性。块语句有两种:begin_end语句——标识顺序执行的语句;fork_join语句——标识并行执行的语句
(1)顺序块
块内的语句是顺序执行的;每条语句的延迟时间是相对于前一条语句的仿真时间而言的;直到最后一条语句执行完,程序流程控制才跳出该顺序块。
begin begin:块名
语句1; 块内声明语句;
语句2; 或 语句1;
... ...
语句n; 语句n;
end end
注:块内声明语句可以是参数声明、reg型变量声明、integer型变量声明、real型变量声明语句。
begin
b = a;
c = b;//实际上是a的值给了c
end
begin
b = a;
#10 c = b;//在两条赋值语句间延时10个时间单位
end
注:这里的标识符“#”表示延时,在模块调用中“#”表示参数传递。
【例】用顺序块和延时控制组合产生一个时序波形
parameter d =50;
reg [7:0] r;
begin
#d r = 'h35;
#d r = 'hE2;
#d r = 'h00;
#d r = 'hF7;
#d r -> end_wave;//触发事件end——wave
end
注:每条语句的延时时间d是相对前一条语句的仿真时间而言的,按顺序执行
(2)并行块
块内的语句是同时执行的;块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间而言的;延迟时间用于给赋值语句提供时序;当按时间排序在最后的语句执行完或一个disable语句执行时,程序流程控制跳出该并行块。
fork fork:块名
语句1; 块内声明语句;
语句2; 或 语句1;
... ...
语句n; 语句n;
join join
注:块内声明语句可以是参数声明、reg型变量声明、integer型变量声明、real型变量声明语句、time型变量声明语句和事件(event)说明语句。
//【例 】
reg [7:0] r;
fork
#50 r = 'h35;
#100 r = 'hE2;
#150 r = 'h00;
#200 r = 'hF7;
#250 r -> end_wave;//触发事件end——wave
join
注:在fork-join块内,各条语句可不按照顺序给出,因为块内语句是同时执行的,不分先后。
在上面的格式中的第二种格式,都存在一个块名,块名就相当于一个标识符,为什么需要块名呢?
可以在块内定义局部变量,即只在块内使用的变量。
可以允许块被其他语句调用,比如disable语句。
在Verilog语言中,所有的变量都是静态的,即所有的变量都只有一个且唯一一个的存储地址,因此进入或跳出块并不影响存储在变量内的值。
五、思考题
- 逻辑运算符与按位逻辑运算符有什么不同,它们各在什么场合使用?
用逻辑运算符运算时是两个操作数进行逻辑运算,而按位逻辑运算符是两个操作数对应的每一位进行逻辑运算。逻辑运算符多用于条件的判断,按位逻辑运算符用于信号的运算和检测。 - 指出两种逻辑等式运算符的不同点,解释书上的真值表。
两种逻辑运算符有很大的区别。“===” 要求两个比较数完全一样,无论高阻还是未知,只要每位完全相同即可。 “= =”只有在两个操作数每位都已知(即0或1),在这种前提下两个操作数每位相同结果才为真,如果不是在这个前提那么其结果始终未x。 - 拼接符的作用是什么?为什么说合理地使用拼接符可以提高程序的可读性和可维护性?拼接符表示的操作其物理意义是什么?
拼接符的作用是把两个或多个信号的某些位拼接起来进行运算操作。因为借助拼接符可以用一个符号名来表示由多位信号组成的复杂信号。其物理意义是将多个信号结合成一个信号。 - 如果都不带时间延迟,阻塞和非阻塞赋值有什么不同?举例说明它们的不同点?
阻塞和非阻塞赋值的区别在阻塞是顺序执行而非阻塞是并行执行。以下面的语句举例
//非阻塞赋值
always @ (posedge clk) begin
b <= a;
c <= b;
end
//阻塞赋值
always @ (*) begin
b = a;
c = b;
end
两种不同的赋值方式结果也是不同的。非阻塞赋值b<=a;c<=b;两条语句是同时执行的,而阻塞赋值b=a;c=b;两条语句先执行b=a后执行c=b,最终的结果是c = a。
- 举例说明顺序块和并行块的不同。
下面两个块执行起来效果是完全一样的,第一个模块是按顺序执行,而第二个模块是每个语句同时执行的。
//顺序块
parameter d =50;
reg [7:0] r;
begin
#d r = 'h35;
#d r = 'hE2;
#d r = 'h00;
#d r = 'hF7;
#d r -> end_wave;//触发事件end——wave
end
//并行块
reg [7:0] r;
fork
#50 r = 'h35;
#100 r = 'hE2;
#150 r = 'h00;
#200 r = 'hF7;
#250 r -> end_wave;//触发事件end——wave
join
- 如果在顺序块中,前面有一条语句是无限循环,下面的语句能否进行?
下面的语句不能执行。 - 如果在并行块中,发生上述情况,会如何呢?
下面的语句可以执行。
作者:xlinxdu
版权:本文是作者读书整理的笔记,部分图片来源于参考教材,侵权联系删。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。