一、起源
在菜鸟教程学习Verilog语法的时候,在移位操作符下面给出了一个实例,就是下面这个:
A = 4'b1100 ;
B = 4'b0010 ;
A = A >> 2 ; //结果为 4'b0011
A = A << 1; //结果为 4'b1000
A = A <<< 1 ; //结果为 4'b1000
C = B + (A>>>2); //结果为 2 + (-4/4) = 1, 4'b0001
总是感觉不对劲,因此在vivado上面跑了一下,越跑越迷糊,这是得到的波形,完全和结果对应不上,最后得到的C结果为5,这是为什么呢???
二、过程
这是我最开始的对变量的定义:
reg [3:0]A;
reg [3:0]B;
reg [3:0]C;
猜想是不是因为没有定义signed类型的原因, 于是对于要应用算术位移的A变量定义为了signed类型,reg signed[3:0] A:
也就是说,其实这儿并没有把算术位移应用进去,然后就越来越混乱,因为结果也没有改变。。。那么算术位移到底在什么情况下会应用呢?
根据实际情况,定义了以下几个变量,同时考虑正负的问题:
reg Clk;
reg signed [3:0]SA;
reg [3:0]B;
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
SA = 4'b1010 ;
B = 4'b1010 ;
# 2000;
SA = -4'b1010;
B = -4'b1010 ;
# 2000;
$stop;
end
运行结果如下:
最开始SA=1010,对于SA本身来说,因为是符号型,最高位为1,因此1010相当于-2,但是因为前面并没有负号,因此还是按照原码的形式存放,所以在内存中直接按照1010存放,B=1010,是无符号型,也是按照1010的格式存放。
但是当SA=-1010的时候,前面的负号对编译器而言,相当于把后面的数值当作负数看,因此在内存中就存的是1010的补码形式,即0110,同理,对于B=-1010的时候也同理
因此,得到结论是,在编译器中,究竟将一个数值按照补码还是原码存放进内存中,取决于前面有没有负号,而不是取决于数据本身。即使定义的是一个符号型变量且最高位为1,那还是按照原码形式存放。
但是到目前还没有探究算术位移到底如何实现?同理,继续延续上述的变量定义,SA为符号型,B为无符号型,测试代码如下:
initial begin
SA = 4'b1010 ;
B = 4'b1010 ;
# 2000;
SA = SA>>>1;
B = B>>>1 ;
# 2000;
$stop;
end
结果如下:
经过测试,可以看到,实际上,只有定义为符号型之后,算术位移才会起作用,否则仍然按照逻辑位移的方式直接位移。
三、总结
- 在实际的信号编译中,在内存中是按照原码显示还是将其当作负数按照补码形式显示,本质上取决于定义数据的时候是否添加了负号,而于定义的数据本身无关。
- 如果想要实现算术位移,那么就要求在定义变量的时候将其定义为符号型变量。如果不加定义,Verilog中默认认为数据是无符号型的,就会导致算术位移仍然按照逻辑位移的方式执行。