了解Verilog中’signed’的作用:处理有符号数
在Verilog中,数据类型'signed'扮演着重要的角色。它用于处理有符号数,为设计者提供了更丰富的表达能力和灵活性。本文将深入探讨Verilog中'signed'在乘法和加法运算中的作用及其用法,并使用无符号器件,搭建一个有符号的乘法器和加法器。
目录标题
1. 了解有符号数:
🍓在计算机中,有符号数和无符号数是两种常见的数据表示形式。有符号数可表示正数、负数和零,而无符号数仅用于表示非负数和零。在数字电路设计中,有时需要处理有符号数。
🍓有符号数采用补码的形式表示,其中最高位为符号位,补码的表示方式如下:
- 对于正数,补码与其原码相同。
- 对于负数,首先取其绝对值的二进制原码,然后将所有位取反(0变为1,1变为0),最后再加1。
例如,假设使用8位二进制来表示整数,我们来看一个负数-5的补码表示:
- 首先,将5的绝对值转换成二进制原码:00000101。
- 然后,将所有位取反得到补码:11111010。
- 最后,加1得到最终的补码表示:11111011。
🍓由上述可知,负数的补码为原码按位取反,再加1,因此 对于同一个负数,用同样的位宽来表示绝对值的原码和补码,则绝对值的二进制原码 + 最终的补码的结果为0,最高位为1
;
例如, 一个负数的绝对值为8bit,补码也为8bit,相加后的结果为9bit,9`b1_0000_0000,
下面举个实例:
-5的绝对值原码为00000101; 补码为11111011;
0000
_
0101
+
1111
_
1011
=
1
_
0000
_
0000
0000\_0101 + 1111\_1011 = 1\_0000\_0000
0000_0101+1111_1011=1_0000_0000
2. signed类型在加法中的作用
🍎设有一个位宽为A_WIDTH的变量a,设有一个位宽为B_WIDTH的变量b,若a,b均为有符号数,由补码的计算过程可知,8bit的负数补码,加上其绝对值原码,对应的运算结果的低8bit为0;
🍎因此可以有以下推论:
- 取A_WIDTH和B_WTDH的最大值C_WIDTH = max(A_WIDTH, B_WIDTH)
- a + b的运算结果最大不会超过C_WIDTH + 1
- 则有符号数a + 有符号数b, 等于将a,b扩展符号位到C_WIDTH + 1,之后相加,只保留运算结果的低 C_WIDTH + 1位,即为有符号数a和b的和;
🍎也就是说signed在加法中的作用是,将a,b扩展符号位(至少扩展1位)到相同位宽后,再进行加法运算
;
3. 用无符号加法器搭建有符号加法器
🍁基于第2节的分析,我们可以有以下有符号数的加法器代码:
module signed_add(a, b, sum_out);
parameter A_WIDTH = 3;
parameter B_WIDTH = 4;
parameter C_WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH + 1 : B_WIDTH + 1;
input [A_WIDTH - 1 : 0] a;
input [A_WIDTH - 1 : 0] b;
output [C_WIDTH - 1 : 0] sum_out;
assign sum_out = {{(C_WIDTH - A_WIDTH){a[A_WIDTH - 1]}}, a} + {{(C_WIDTH - B_WIDTH){b[B_WIDTH - 1]}}, b};
endmodule
🍁运算结果与sum = $signed(a) + $signed(b)相同;
4. signed类型在乘法中的作用
🍏设有一个位宽为A_WIDTH的变量a,设有一个位宽为B_WIDTH的变量b,若a,b均为有符号数;则a x b可以被拆分为 a的绝对值 x b的绝对值,若a,b同符号,则绝对值的乘积等于a x b, 否则结果为负数, 需要将其转换为补码形式
🍏有以下4种情况(a_abs为a的绝对值,b_abs为b的绝对值):
-
- a, b均为正数,则 a ∗ b = a [ A _ W I D T H − 1 : 0 ] ∗ b [ B _ W I D T H − 1 : 0 ] a * b = a[A\_WIDTH - 1 : 0] * b[B\_WIDTH - 1 : 0] a∗b=a[A_WIDTH−1:0]∗b[B_WIDTH−1:0];
-
- a为正数,b为负数, 则a的绝对值a_abs = a[A_WIDTH - 1 : 0]; b的绝对值为~b[B_WIDTH - 1 : 0] + 1,也就是1<<B_WIDTH- b[B_WIDTH - 1 : 0]
因此a_abs x b_abs = a[A_WIDTH - 1 : 0] << B_WIDTH - a[A_WIDTH - 1 : 0] x b[B_WIDTH - 1 : 0], 由于运算结果是负数,因此还要对其进行取反 + 1的运算,因此运算结果为(位宽A_WIDTH + B_WIDTH,扩展位补0): a ∗ b = 1 < < ( A _ W I D T H + B _ W I D T H ) − a [ A _ W I D T H − 1 : 0 ] < < B _ W I D T H + a [ A _ W I D T H − 1 : 0 ] ∗ b [ B _ W I D T H − 1 : 0 ] a * b = 1 << (A\_WIDTH + B\_WIDTH) - a[A\_WIDTH - 1 : 0] << B\_WIDTH + a[A\_WIDTH - 1 : 0] * b[B\_WIDTH - 1 : 0] a∗b=1<<(A_WIDTH+B_WIDTH)−a[A_WIDTH−1:0]<<B_WIDTH+a[A_WIDTH−1:0]∗b[B_WIDTH−1:0];
- a为正数,b为负数, 则a的绝对值a_abs = a[A_WIDTH - 1 : 0]; b的绝对值为~b[B_WIDTH - 1 : 0] + 1,也就是1<<B_WIDTH- b[B_WIDTH - 1 : 0]
-
- 同理,若a为负数,b为正数运算结果位宽为A_WIDTH + B_WIDTH a ∗ b = 1 < < ( A _ W I D T H + B _ W I D T H ) − b [ B _ W I D T H − 1 : 0 ] < < A _ W I D T H + a [ A _ W I D T H − 1 : 0 ] ∗ b [ B _ W I D T H − 1 : 0 ] a * b = 1 << (A\_WIDTH + B\_WIDTH) - b[B\_WIDTH - 1 : 0] << A\_WIDTH + a[A\_WIDTH - 1 : 0] * b[B\_WIDTH - 1 : 0] a∗b=1<<(A_WIDTH+B_WIDTH)−b[B_WIDTH−1:0]<<A_WIDTH+a[A_WIDTH−1:0]∗b[B_WIDTH−1:0]扩展位补1;
-
- 若a,b均为负数,则a x b 为 1<<(B_WIDTH -1)- b[B_WIDTH - 2 : 0]和 1<<(A_WIDTH -1)- a[A_WIDTH - 2 : 0] a ∗ b = 1 < < ( A _ W I D T H + B _ W I D T H ) − b [ B _ W I D T H − 1 : 0 ] < < A _ W I D T H − a [ A _ W I D T H − 1 : 0 ] < < B _ W I D T H + a [ A _ W I D T H − 1 : 0 ] ∗ b [ B _ W I D T H − 1 : 0 ] a * b = 1 << (A\_WIDTH + B\_WIDTH) - b[B\_WIDTH - 1 : 0] << A\_WIDTH - a[A\_WIDTH - 1 : 0] << B\_WIDTH+ a[A\_WIDTH - 1 : 0] * b[B\_WIDTH - 1 : 0] a∗b=1<<(A_WIDTH+B_WIDTH)−b[B_WIDTH−1:0]<<A_WIDTH−a[A_WIDTH−1:0]<<B_WIDTH+a[A_WIDTH−1:0]∗b[B_WIDTH−1:0]扩展位补0;
🍏由以上分析结果可知,有符号数的乘法和无符号数的乘法差异还是很大的,因此像加法器一样难以通过简单的扩位运算来实现,因此在综合时,大概率有符号乘法和无符号乘法运算调用的不同的乘法器
;
5. 用无符号运算器件,搭建signed有符号数的乘法器
module signed_add(a, b, mul_out);
parameter A_WIDTH = 4;
parameter B_WIDTH = 5;
parameter C_WIDTH = A_WIDTH + B_WIDTH;
input [A_WIDTH - 1 : 0] a;
input [A_WIDTH - 1 : 0] b;
output [C_WIDTH - 1 : 0] mul_out;
wire [A_WIDTH + B_WIDTH : 0] mul_out0;
wire [A_WIDTH + B_WIDTH - 1 : 0] mul_out1;
wire [A_WIDTH + B_WIDTH - 1 : 0] mul_out2;
wire [A_WIDTH + B_WIDTH - 1 : 0] mul_out3;
assign mul_out0 = a[A_WIDTH - 1] | b[B_WIDTH - 1] ? 1 << (A_WIDTH + B_WIDTH) : 'd0;
assign mul_out1 = a[A_WIDTH - 1] ? b << A_WIDTH : 'd0;
assign mul_out2 = b[A_WIDTH - 1] ? a << B_WIDTH : 'd0;
assign mul_out3 = a[A_WIDTH - 1 : 0] * b[B_WIDTH - 1 : 0];
assign mul_out = mul_out0 - mul_out1 - mul_out2 + mul_out3;
endmodule
🍉运算结果与mul = $signed(a) * $signed(b)相同;
🍉验证1: a = -4; b= 3; 则运算结果为-12; a用4bit表示为 1100, b用5bit表示为 00011;
- mul_out0为1<<9, 即512;
- mul_out1为3 << 4 , 即48;
- mul_out2为0;
- mul_out3为 1100 * 00011 =12 * 3 = 36;
- 因此乘法器运算结果为512-48+36 = 500, 也就是1_1111_0100(保留9bit)
- 而1_1111_0100就表示-12;
🍉验证2: a = 4; b= -3; 则运算结果为-12; a用4bit表示为 0100, b用5bit表示为 11101;
- mul_out0为1<<9, 即512;
- mul_out1为0;
- mul_out2为4<<5 ,为128;
- mul_out3为 0100 * 11101 =4 * 29 = 116;
- 因此乘法器运算结果为512-128+116 = 500, 也就是1_1111_0100(保留9bit)
- 而1_1111_0100就表示-12;
🍉验证3: a =-4; b= -3; 则运算结果为12; a用4bit表示为 1100(12), b用5bit表示为 11101(29);
- mul_out0为1<<9, 即512;
- mul_out1为29<<4, 为464;
- mul_out2为12<<5 ,为384;
- mul_out3为 1100 * 11101 =12 * 29 = 348;
- 因此乘法器运算结果为512-464-384+348 = 12;
🍉因此,我们用无符号运算器搭建的有符号数的乘法器,可以用于有符号数的运算;
6. signed使用中的其他注意事项
- signed类型的数若和无符号数进行运算,则一律按无符号数进行运算;
- 运算结果是否为有符号数,与是否将运算结果定义为了有符号数无关;
7. 结论:
在Verilog中,'signed’类型为处理有符号数提供了必要的功能和灵活性。它使设计者能够轻松地声明、操作和处理有符号数,并确保在数字电路实现中正确处理符号位和数值范围。通过充分理解和应用’signed’类型,设计者可以更好地满足设计需求,实现高质量的数字电路设计。。