9.1.4关系运算符
说到关系运算符,无非有以下四种:
<:小于
>:大于
<=:小于或等于
>=:大于或等于
字面意思都很好理解,可是在Verilog中进行这样运算的结果是什么呢?它的结果仍然只有一位:0、1、X。在进行关系运算时,会根据运算的结果返回一个逻辑值,当运算的结果为真,则返回1;当运算的结果为假,则返回0;当某个操作数的值不定时,则两个操作数的关系就不能确定,返回值当然也是不确定的。世界上不确定的东西太多,就连硬件语言中也存在着各种的不确定性啊,不管怎样,生活还是要继续的,运算也还是要执行的。例如:
reg[3:0]A=4’b1100;
reg [3:0]B=4’b0101;
reg [3:0]C=4’b00X1;
result1=(A>B); // result1=1
result2=(B>=A); // result2=0
result3=(A>C); // result3=X
result4=(B<C); // result4=X
对上述结果进行仿真,结果如下:
上例中,操作数C为不确定值,无法进行大小的比较,所以result3返回值为X,同样,对于B和C的大小关系,C中X的值不能确定,所以当B和C进行关系运算时,无法确定其真假,返回值为X,如图中红线所圈,result3和result4都是不确定值X。看来对于操作数中有X或Z时,我们都要特别关照一下,指不定什么时候就让X或Z挖个坑让自己掉下去。
9.1.5等式运算符
等式应该是我们在小学数学里接触到的第一个运算符吧,在Verilog中,等式运算符有4个家庭成员:
==:等于
!=:不等于
===:等于
!==:不等于
注意,“=”在Verilog中表示赋值,并不是等式运算的意思,后面我们会再详细介绍。那还是有人会问,==和===这两个等于有什么不同啊?!=和!==这两个不等于又有什么不同啊?心急吃不了热豆腐,还是听我慢慢说来吧。
“==”是逻辑等,“===”是case等(也叫严格等),常用于case语句中。我们先看看它们的真值表,再举例说明。真值表如下所示。
逻辑等(==) |
0 |
1 |
X |
Z |
0 |
1 |
0 |
X |
X |
1 |
0 |
1 |
X |
X |
X |
X |
X |
X |
X |
Z |
X |
X |
X |
X |
case等(===) |
0 |
1 |
X |
Z |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
X |
0 |
0 |
1 |
0 |
Z |
0 |
0 |
0 |
1 |
从上表中,我们可以看出,逻辑等(==)的运算结果有0、1、X三个值,case等(===)的运算结果只有0、1两个值。对于“==”来说,操作数中若只有0或者1,并且两个操作数对应位都相等,则返回值是1,但若操作数对应位不相等,则返回值是0;可是,当操作数中有X或者Z时,“==”运算将X或Z视为不确定值,无法进行两个操作数等于或者不等于的比较,所以返回结果都是X。对于“===”来说,操作数中若只有0或者1,其规则与“==”相同,但是当操作数找那个有X或者Z时,“===”只是将X或者Z视为一个符号,没有对应到它是为0或1,而是认为两个操作数都是“X”这个符号,它就认为是相同的。所以,归根结底,“==”和“===”的差异还是表现在特殊的“X”和“Z”上。我们来实战演练一下,例1:
2’bX0==2’bX1 //值为0,因为等式两边肯定不相等
2’bX0==2’bX0 //值为X,因为等式两边操作数中的X可能为0或者1,
两操作数不一定相等
2’bX0===2’bX1 //值为0,虽然两个操作数高位都为X,但低位不相同
2’bX0===2’bX0 //值为1,两个操作数相同
高位都为X(不管是0还是1),低位都为0
例2:
if(datain==1’bX)
dataout1<=1’b1;
else
dataout1<=1’b0;
if(datain===1’bX)
dataout2<=1’b1;
else
dataout2<=1’b0;
上例中,若datain确实是1’bX,dataout1和dataout2各输出什么,应该不言而喻了吧!You are right!dataout1的值为0,dataout2的值为1。
所以,逻辑等与不等(==、!=)会计较X或Z的取值,而case等与不等(===、!==)只是把X或Z当作一个符号,只要是这个符号,就认为等,不是这个符号,就认为不等,所以,逻辑等与不等和case等与不等的主要差别在于对待X或Z值的态度上。在实际中,电路认不出X,更别说去判别X,所以就无法严格等了(也就是case等),因此,case等与不等(===,!==)只能用于行为描述,不能用于RTL描述,即综合工具是不支持这种描述方式的。
9.1.6 移位运算符
移位运算符是编程中常见的运算符,在C语言中也有。Verilog中有两种移位运算符:
<<:左移运算符
>>:右移运算符
它的使用很简单,A<<n,表示把操作数A左移n位;B>>m,表示把操作数B右移m位,其中,m只能是无符号整数,表示移位的位数。例如:
reg[7:0]num=8’b00110101; //无符号数num
parameter signed [7:0]num_signed=8’b11010110; //有符号数num_signed
reg [7:0]result1;
reg [7:0]result2;
reg [7:0]result3;
reg [7:0]result4;
reg [9:0]result4;
reg [9:0]result5;
reg [9:0]result6;
reg [9:0]result7;
result1 = (num<<3); //result1=8’b10101000
result2 = (num>>4); //result2=8’b00000011
result3=(num_signed<<3); // result3=8'b10110000
result4=(num_signed>>4); // result3=8'b00011010
result4= (num<<3); //result4=10’b0110101000
result5 = (num>>4); //result5=10’b0000001101
result6=(num_signed<<3); // result6=10'b1010110000
result6=(num_signed>>4); // result6=10'b0000111101
仿真结果如图所示:
图中蓝线所圈(result1、result2、result5、result6)表示无符号数进行运算的结果,红线所圈(result3、result4、result7、result8)表示有符号数的结果。若结果变量与操作数的位宽相同,则直接进行移位运算,如图中result1、result2、result3、result4所示,无论是无符号数的运算结果(result1、result2)还是有符号数的运算结果(result3、result4)都是在空缺位置补0,即右移在高位补0,左移在低位补0;若结果变量与操作数的位宽不同,则需要先进行位宽扩展或压缩,在进行移位与运算,如图中result5、result6、result7、result8所示,但是这个时候要注意了,无符号数的结果(result5、result6)与有符号数的结果(result7、result8)存在明显差异,对于无符号数而言,位宽的扩展就是补0,而对于有符号数,则是补符号位,比如说上例中parameter signed [7:0]num_signed=8’b11010110在移位前先扩展为10’b1111010110,再左移或右移,此时移位的空缺还是补0。
所以,移位运算符对有符号数和没符号数也不是那么敏感的,虽然我们能声明操作数是有符号的还是没符号的,但是移位运算符并不智能,在结果变量与操作数位宽相同时,我们需要手动完成带符号的移位,但需要借助下面要介绍的位拼接运算符来完成,在这里我们先保留一下。
9.1.7位拼接运算符
很多时候,我们不需要一个数据中所有的信息,可能需要数据a的一部分信息,需要b的一部分信息,然后要把它们合在一起,就像吃PIZZA,我们想吃多种口味,就会选择拼盘,在Verilog中,也提供了这样将各个数据中某几位拼接起来组成新数据的运算符,位拼接运算符。有两种形式:
{}:位拼接
{{}}:复制
见下例:
reg[7:0]data1=8’b11010100;
reg [9:0]data2=10’b1001011101;
reg [7:0]dataout;
{data1[3:0],data2[9:7]}:表示取data1的低4位,data2的高三位组合成新的数据
{2{data1}}:表示将data1复制两遍得到16位新数据,即16’b1101010011010100
{data2[1:0],2{data1}}:表示取data2的低两位,并且把data1复制一遍,
即18’b011101010011010100
但是,在位拼接运算符中也存在陷阱,一不小心就掉进去,尤其是对于懒人来说,看下例:
{‘b100,’d5,8}:错误,这三个数都没有指明位宽
{4{‘b11}}:错误,’b11位宽不明确
在仿真中,当出现上例中的表达式时,就会有错误报出,如上图所示。
所以,在位拼接运算符中,不限制操作数的个数,但最重要的是操作数的位宽,必须严格说明参与拼接运算的每个操作数的位宽。
还记得在移位运算符小节中我们提到的当结果变量与操作数位宽相同时,我们需要借助拼接运算符来手动完成带符号的移位吗?现在可以揭晓谜底,其实大家应该都已经猜到了。还是以parameter signed [7:0]num_signed=8’b11010110为例,当需要带符号右移2位时,可以通过{num_signed [7],num_signed [7],num_signed [7:2]}实现,结果为8’b11110101。
文章评论(0条评论)
登录后参与讨论