第9章拿下运算符(1)
万丈高楼平地起,我们有了常量和变量的基础,也就知道在Verilog中怎么表示我们需要的数据,可是想要达到我们的目的,只有数据是远远不够的,还需要将这些数据按照一定的运算组合起来,才能会当凌绝顶,一览众山小,实现我们需要的功能。那么,在Verilog中,都有些什么运算符呢?它们又到底是怎么工作的呢?其实,这些运算符对于有过编程经历的人来说是很熟悉的,它跟C语言中的很类似,但有许多地方则是完全不同的,比如缩减运算符、位拼接运算符、阻塞非阻塞赋值运算等等。下面我们就一起来看看这些大大小小的运算符吧。
9.1林林总总的运算符
9.1.1逻辑运算符
一说到逻辑运算符,立马就能想到是逻辑与、逻辑或、逻辑非。BINGGO!确实如此。
逻辑与:&&
逻辑或:||
逻辑非:!
上述三种运算符的真值表如下表所示。
表逻辑运算符
a |
真 |
真 |
假 |
假 |
b |
假 |
真 |
假 |
真 |
!a |
假 |
假 |
真 |
真 |
!b |
真 |
假 |
真 |
假 |
a&&b |
假 |
真 |
假 |
假 |
a||b |
真 |
真 |
假 |
真 |
表中,各种真真假假不期而遇,令人眼花缭乱。大家看到这种逻辑运算符,第一反应就是各种真假条件之间的运算关系,而真假逻辑容易让我们想到数字电路中的“1”和“0”,但在实际情况中有可能并不是简简单单的“1”和“0”,我们还是通过实际的例子来鉴定一下真真假假的运算关系吧。
modulelogicmode{
clk,
rst,
dataout
};
inputclk;
inputrst;
outputregdataout;
reg [3:0]temp1=4’b0010; // 逻辑值为1
reg [3:0]temp2=4’b0000; // 逻辑值为0
reg [3:0]temp3=4’b1ZX0; // 逻辑值为1
reg [3:0]temp4=4’b00XZ; // 逻辑值为X
reg data1,data2,data3,data4;
always @(posedgeclk)
begin
if(rst)
dataout<=0;
else
begin
data1<=(!temp1); // data1=0
data2<=(temp1 && temp2); // data2=0
data3<=(temp2 || temp3); // data3=1
data4<=(temp2 || temp4); // data4=X
dataout<=((!data2) && (temp3)); // dataout=1
end
end
endmodule
在Modelsim中仿真结果如下所示:
如图中蓝色线圈中信号所示,data1=0,data2=0,data3=1,data4=X,dataout=1。
从上例可以看出以下几点:
(1)逻辑运算符&&和||是双目运算符,需要两个操作数参与运算,逻辑运算符!是单目运算符,只需要一个操作数;
(2)逻辑运算符只对逻辑值进行操作,若操作数中全为0,则该操作数代表逻辑值0,若操作数中有一位1时,则该操作数代表逻辑值1,若操作数中只包含0、X、Z,则该操作数代表逻辑值X;
(3)逻辑运算符的运算结果为1位:0、1、X。
特别说明:在不清楚运算符优先级的情况下,建议还是带上括号吧,避免由于优先级的问题而导致程序运算结果的错误。不要着急,在熟悉了各种运算符之后我们再给它们排个座。
9.1.2按位运算符
上面的逻辑运算符是对逻辑值进行操作的,按位运算符,顾名思义就是对数据中对应的每一位即每bit进行操作,很多人在不经意间就搞混了。
按位与:&
按位或:|
按位非:~
真值表如下所示。
按位与(&) |
0 |
1 |
X |
Z |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
X |
X |
X |
0 |
X |
X |
X |
Z |
0 |
X |
X |
X |
按位或(|) |
0 |
1 |
X |
Z |
0 |
0 |
1 |
X |
X |
1 |
1 |
1 |
1 |
1 |
X |
X |
1 |
X |
X |
Z |
X |
1 |
X |
X |
按位非(~) |
0 |
1 |
X |
Z |
|
1 |
0 |
X |
X |
其实,看似复杂的真值表,小结一下就很简单:
任何逻辑值(0、1、X、Z)和“1”进行“或”操作,得到的结果都是1;
任何逻辑值(0、1、X、Z)和“0”进行“与”操作,得到的结果都是0;
有逻辑值“Z”参与的运算只能得到逻辑值0、1、X;
除了(1)(2)两种情况外,只要有X或Z参与的逻辑运算,结果都为X。
例如:
reg [5:0]a=6’b011101;
reg [5:0]b=6’b101X00;
reg [5:0]c=6’b10011Z;
reg [5:0]d=4’b1101;
result1=(a&b); // result1=6’b001X00
result2=(b|c); // result2=6’b10111X
result3=(~c); // result3=6’b01100X
result4=(c|d); // result4=6’b101111
上述运算的仿真结果如图所示:
上例中,result4是对两个位数不同的操作数进行运算,结果的位数与c的位数相同(6位),我们从仿真结果中也可以看出,在运算过程中,先把d扩展到6位(图中红线所圈),再按位或,得到的结果为6位(图中蓝线所圈),即当两个操作数位数不同时,位数少的操作数0扩展到相同的位数,也就是我们常说的少数服从多数嘛,而运算结果的位数与位数多的操作数相同。
所以,我们可要睁大眼睛看清楚咯,按位运算符是对数据中所有位进行操作,而逻辑运算符是对整个数或者表达式的逻辑值进行操作,比如逻辑非(!)的结果为一位(0、1、X),但是按位非(~)的结果却与操作数的位数相同,不一定是一位。
9.1.3 缩减运算符
缩减运算符也叫一元归约操作符,其符号和上面介绍的按位运算符相同:
与:&
或:|
异或:^
与非:~&
或非:~|
异或非:^~或者~^
虽然缩减运算符是对数据中每位进行操作,不同的是,它是单目运算符,只有一个操作数,是对操作数中每位与相邻的下一位进行运算,即先把最高位与次高位进行与、或等操作,再将结果与次高位的下一位进行与、或等运算,依次类推,最终得到结果,这个结果是一位的,和按位运算符有着明显的差别。上述缩减运算符中常用的有与(&)、或(|)、异或(^),例如:
reg [3:0]num=4’b0110;
reg out;
out1=# //相当于进行(((num[3]&num[2])&num[1])&num[0]),out=0;
out2=~# //相当于进行(~(((num[3]&num[2])&num[1])&num[0])),out=1;
从上例中可知,缩减运算符是对数据内部的位进行操作,只涉及到一个操作数,并且没有“非”运算,与按位运算符有着本质的区别,不要混淆咯。大家如果对缩减运算符感兴趣,可以自己下去做做仿真,说不定还能挖出宝藏,哈哈。
用户377235 2013-7-20 15:43
北京航空航天大学夏老师的书已经写得十分清楚。有必要再抄写一遍变成“新”书吗?
用户961355 2013-5-14 09:02
用户427466 2013-5-9 22:51
用户427466 2013-5-9 22:49
用户961355 2013-4-27 14:05
用户377235 2013-3-21 04:38
用户433381 2013-3-3 18:36