在FPGA中能够自定义数位宽,这带来了极大的灵活性(相较于C语言等其余语言),可是同时带来的问题就是如何解读这些数据,以及如何处理好他们的进位关系。(以前的某个项目中,就是由于计算位数错误,致使项目结果的错误,debug好久)所以写下这篇blog,对这部分做出必定的总结。git
目前我经常使用的是unsigned int,signed int 以及 fixed-point三种。下面分别介绍。github
这种是最最简单的表示方法。debug
一列数:code
\(a_n a_{n-1}... a_2 a_1a_0\)blog
他的大小等于get
\(D=\sum_{i=0}^{n} a_i 2^i\)it
如: b11100111module
其大小等于
\(2^7+2^6+2^5+2^2+2^1+2^0=231\)语法
很是常见的有符号整数的表示方法,一般的ADC的输出就是这种形式。一般是用补码表示。bug
最高位为符号位,剩下的部分表示数字
定义是这样的:
- 正数的补码等于原码
- 负数的补码=负数的反码+1
- 负数的反码=符号位为1,剩下的部分为正数的取反
若是晕了就看下面的例子
比方说-3用8位Signed int表示为
# 3的Signed int 0000_0011 # 3的反码 1111_1100 # 3的补码 1111_1101 # 为0xfd
验证一下:
module invert( output signed [7:0] out, output signed [7:0] out1, ); assign out = -3; assign out1 = 3; `probe(out); // 这个是iverilog用于实现波形的语法 `probe(out1); endmodule
结果为:
定点数顾名思义就是小数点的位置在二进制数中是固定的,一般须要本身定义。
如总位宽8位的,3位小数,1位符号位表示为,其中\(\Delta\)表示小数点
\(S_4 a_3 a_2 a_1 a_0 \Delta a_{-1} a_{-2} a_{-3}\)
原码的话,计算方式同Unsigned int
\(D=(-1)^{S_4}\sum_{i=-3}^{3} a_i 2^i\)
反码、补码都同Signed int,注意补码在最低位+1
举例: -1.75 Fixed point 总位宽8位的,3位小数,1位符号位表示为
# 原码 1 0001.110 # 反码 1 1110.001 # 补码 1 1110.010 # 0xf2
验证:verilog自己不支持Fixed point,这里采用更加高级的SpinalHDL来验证,具体关于SpinalHDL的能够本身看看
数的表示,特别是负数的表示,采用补码的形式,这种方式更容易计算加减法,而采用原码的表示,更容易计算乘法。
在运算的时候 位宽是会变化的,所以须要注意变化的规律
在两个位宽为B的加减运算中,要保证数据不溢出,结果的位宽应为B+1
在SpinalHDL中,采用 +^ 或 -^ 来保证自动推断位宽
乘法结果的位宽等于被乘数两个位宽之和
除法尽可能使用IP核实现