原文链接(相关文章合集): OFDM 802.11a的xilinx FPGA实现
1.前言
前面OFDM 802.11a的FPGA实现中的文章在涉及到加窗操作的时候,使用到了移位操作,但是没有考虑到有符号数的问题,今天发现了这一问题,特详细对verilog中移位操作时,有符号数和无符号数的区别进行讲解,并且设计补码、反码、原码的知识。
2.Verilog的移位运算符分类:
逻辑移位:>>/<<。
逻辑左移右移代表不管符号位,整体做移动。
算数移位:>>>/<<<。
算术左移右移代表保留符号位不动。
两者都是非循环的移位操作。
若移位赋值目标位数多于源数据,对于有符号数右移,先拿符号位填充多出的bit位再按照下面的移位运算方式进行运算。
对于无符号数,逻辑移位和算数移位的效果完全一致。空缺拿0来补充。
2.1算数移位
算数右移(>>>)
当移位数据为有符号数,高位补符号位。
当移位数据为无符号数,高位补0。
算数左移(<<<)
有符号数与无符号数效果一致,空缺拿0来补充。
2.1逻辑移位
逻辑右移(>>)
不论移位数据为有符号数、无符号数,高位补零。
逻辑左移(<<)
有符号数与无符号数效果一致,空缺拿0来补充。
2.Verilog中的移位运算符易错点
对于左移,有符号数与无符号数效果一致,空缺拿0来补充,唯一需要注意的是溢出问题,这个使用的时候很容易想到,不多做赘述。
而对于逻辑右移:“>>”
不区分无符号和有符号移位,即对有符号和无符号数进行">>"操作结果一致。
对有符号数使用">>“进行右移,高位补"0”;
对无符号数使用">>“进行右移,高位补"0”;
**如果有符号数,直接使用“>>”,会导致计算结果错误,比如:
a = -6;
//其原码为: 4'b1110;
//负数以补码形式存储(原码除符号位取反+1),
//补码为4'b1010
//若
b = a >> 1;
//得到
b = 4'b0101;
//此时b = 5,正确的值应该是-3才对
对于算术右移:“>>>”
区分无符号和有符号移位,即对有符号和无符号数进行">>>"操作结果不一样。
对有符号数使用">>>"进行右移,高位补符号位;
对无符号数使用">>>“进行右移,高位补"0”;
a = -6;
//其原码为: 4'b1110;
//负数以补码形式存储(原码取反+1),补码为4'b1010
//若
b = a >>> 1;
//得到
b = 4'b1101;
//此时b的原码为(补码除符号位取反+1):
//4'b1011,对应的值为-3
//此时的结果才是正确的
但是,你以为只是简单的在你想得到有符号的结果时用“>>>”就会正确吗?那就大错特错了!!!!
之前做的设计,在加窗时需要用到除以2,即右移一位操作,我简单的使用了“>>”。由于只对其中个别数据进行这一操作,运气好,没遇到负数,所以之前仿真的时候也没出现问题。
OFDM802.11a的FPGA实现(十三)加窗(含verilog和matlab代码)
OFDM802.11a的FPGA实现(十五)短训练序列:STS(含Matlab和verilog代码
直到后面在设计长训练序列(LTS)时,遇到了负数,此时怎么也对不上结果。
STS_dout <= {Short_Mem[0][15:8]>>1,Short_Mem[0][7:0]>>1};
即使改成算术右移:“>>>”也不管用。
STS_dout <= {Short_Mem[0][15:8]>>>1,Short_Mem[0][7:0]>>>1};
因为如果操作数是无符号的,使用“>>”和">>>“都是逻辑移位,会导致结果错误。
3.定义数据类型为signed或使用$signed
在verilog中有时会用signed修饰符来修饰定义的数据,运算的时候也会用 s i g n e d ()任务来强制转换数据,那么 s i g n e d 的修饰是为什么呢,是为了区分有符号数和无符号数的加法和乘法吗?其实不是的,因为有符号数和无符号数据在做运算时,电路结构是一样的, signed()任务来强制转换数据,那么signed的修饰是为什么呢,是为了区分有符号数和无符号数的加法和乘法吗?其实不是的,因为有符号数和无符号数据在做运算时,电路结构是一样的, signed()任务来强制转换数据,那么signed的修饰是为什么呢,是为了区分有符号数和无符号数的加法和乘法吗?其实不是的,因为有符号数和无符号数据在做运算时,电路结构是一样的,signed()的真正作用是决定如何对操作数扩位的问题。
对于verilog中的移位操作signed修饰的时候,高位会进行补符号位操作。
对于verilog中的加法和乘法操作,会先对操作数据扩位成结果相同的位宽,然后进行加法或者乘法处理。比如a/b都为4位数据,c为5位数据,c = a + b,这个运算的时候会先把a和b扩位成5位,然后按照无符号加法进行相加。a/b没有被signed修饰的时候会按照无符号数的扩位方式进行扩位,即高位补0,加法的结果当然也是a、b为无符号数相加的结果。
所以说正确的代码应该如下:
STS_dout <= {$signed(Short_Mem[0][15:8])>>>1,$signed(Short_Mem[0][7:0])>>>1};
前面展示的代码链接:
OFDM802.11a的FPGA实现(十三)加窗(含verilog和matlab代码)
OFDM802.11a的FPGA实现(十五)短训练序列:STS(含Matlab和verilog代码
需要更正这节所提到的问题,才能保证后期不出错误。
原文链接(相关文章合集):OFDM 802.11a的xilinx FPGA实现