本文转载自:FPGA入门到精通微信公众号
FPGA开发中涉及到数学运算,那理解和掌握无符号数和有符号数就非常重要了,如果用错了,就容易出一些奇怪的错误。
今天我们来聊一聊verilog中的“有符号数”和“无符号数”。
文末会总结使用要点给大家参考。
一、原码、反码、补码
1、原码
原码是数字在计算机中的二进制表示形式,包括符号位和数值位。
对于正数,原码与其真值(即十进制数)相同。
对于负数,原码是其在补码表示下的反码再加 1。
2、反码
反码是原码的符号位除外,其余位取反。
无论正负,反码都是原码的反转。
3、补码
补码是反码再加 1。
对于正数,补码与原码相同;
对于负数,补码是其在反码表示下的加 1。
二、有符号数与无符号数
实际上FPGA运行逻辑中是不区分二进制数是“正”还是“负”的,它就是一个二进制数而已,对于正数或无符号数,存储的就是原码,对于负数,存储是补码。
1、有符号数
在有符号数中,最高位(最左边的位)用作符号位,1 表示负数,0 表示正数。
例如,一个 8 位的有符号整数可以表示范围为 -128 到 127 的整数。
2、无符号数
无符号数则没有符号位,所有的二进制位都用于表示数值。
无符号整数的取值范围通常是从 0 到 2 的 n 次方 -1,其中 n 是二进制位的位数。
例如,一个 8 位的无符号整数可以表示范围为 0 到 255 的整数。
三、加法与减法
1、加法运算
可以使用位运算或者真值运算实现。
写代码时要注意进位,防止溢出,另外有符号数还要注意位宽问题,可能需要复制符号位。
无符号数计算:
reg [7:0] a = 127;
reg [7:0] b = 127;
reg [8:0] c;
always@(posedge clk)
c <= a + b;
有符号数计算需要注意复制符号位
reg [7:0] a = -5;
reg [7:0] b = -6;
reg [8:0] c;
always@(posedge clk)
c <= {a[7], a} + {b[7], b};
2、减法运算
减法运算,实质是 a-b可转换为 a + b[补码]。
和加法一样,写代码时要注意进位,防止溢出,另外有符号数还要注意位宽问题,可能需要复制符号为。
四、Verilog中的Signed本质及用法
1、定义
在verilog中,有符号数可以加“signed”关键词来修饰,表示这个寄存器或变量是有符号数,没有加“signed”修饰,默认就是无符号数。
例如:
reg signed [7:0] a; // 定义一个 8 位的有符号整数变量 a
2、数学运算处理
在进行加法、减法、乘法等运算时,有符号数和无符号数会有不同的处理方式。
加了signed关键词的本质是运算时,会先将数据扩位至相同的位宽,然后按照有符号数的运算规则进行处理。
这里要注意了,有符号数和无符号数不能混合运算,否则容易出错;
(1)如果运算时,变量都加了signed关键词。
reg signed [7:0] a = -10;
reg signed [7:0] b = 5;
reg signed [8:0] c;
always@(posedge clk)
c <= a + b;
仿真结果:
分析:结果正确。
(2)如果运算时,无符号和有符号混合运算了。
reg signed [7:0] a = -10;
reg [7:0] b = 5;
reg signed [8:0] c;
always@(posedge clk)
c <= a+b
仿真结果:
分析:实际是按无符号数运算的。
五、总结
无符号数运算时,主要注意防止出现结果溢出。
有符号数运算是,注意符号位的处理,可以加关键字“signed”,也可以自己控制符号位复制来参与运算。
有符号数和无符号数不要混合运算,否则容易出错。