数字设计之二进制数运算

原码

无符号数:所有bit都是数值位

例如:0111->7, 1111->15

有符号数:最高位是符号位,其余bit是数值位

例如:0111->7, 1111->-7

反码

无符号数:改变数中的每一位得到反码;

例如:0111的反码是1000

有符号数,符号位不变,数值位取反得到反码;

补码

有符号数,由原码的反码加1得到补码;

例如:1111(-7)的补码是1001(-7)

无符号数,原码与补码相同

在计算机系统中,数值一律用补码来表示和存储,原因在于,使用补码,可以将符号位和数值域统一处理,同时,加法和减法也可以统一处理

有符号数

在反码形式中,负数是相应正数的反码

例如:0111->7, 1000->-7

在反码形式中有个特殊的值-0。原因是0000表示0, 1000也表示0。

在补码形式中,负数是相应正数的补码,反过来也成立,正数是相应负数的反码。

例如:0111->7, 1001->-7

计算机在所有的算术运算中都使用补码来表示负整数,原因是减去某个数和加上这个数的补码是一样的。

Tips: 给定一串二进制数口算成十进制数,先问是否有符号数,如果是无符号数,则与码制无关;若是有符号数,再问码制。

例如:给定1111。

若是无符号数,则对应的十进制数是15;

若是有符号数,反码,则对应的十进制数是-7;

若是有符号数,补码,则对应的十进制数是-1

范围

二进制数的数值范围取决于数的位数。

对于无符号数,n位数的范围是0-2^n-1

对于有符号数(补码),n位数的范围是 -2^(n-1)-2^(n-1)-1

例如:4bit 无符号数,范围是0-15

4bit有符号数,范围是-8-7.

溢出

在数字电路设计中,每一数都会用reg或wire类型存放。定义的位宽不够,很容易在计算过程中发生溢出。

例如:1010 + 1010 = 1 0100

补位

Verilog计算过程中,我们通常会主动会数进行扩位。

对无符号数扩位,只需最高位补0。对无符号数进行扩位,通常用于将无符号数转成有符号数。

例如 4bit 无符号数1010,扩位成5bit,->0 1010

对有符号数扩位,最高位需补符号位。

例如:4bit 有符号数1010,扩成5bit, -> 1 1010

Verilog中,无论加减,两个数都应扩成与结果位宽一致再进行加减。

例如:1010+1010= 1 0100

若是无符号数,应写成,0 1010(10) + 0 1010(10) = 1 0100(20)

若是有符号数,应写成,1 1010(-6) + 1 1010(-6) = 1 0100(-12)

加法

Verilog中,为防止数据溢出。两个位宽为a和位宽为b的数据相加,其运算结果需用 max{a,b} +1位宽的变量来存放。

例如,两个4bit数相加,运算结果用5bit数存放一定不会溢出。

一个4bit数,一个5bit数相加,运算结果用6bit数存放一定不会溢出。

无论是有符号数还是无符号数,加法运算对位宽的要求都如上。

例如:4bit有符号数的范围是(-8,7),两个数相加后的范围是(-16, 14)。5bit有符号数的范围是(-16,15)。因此两个4bit有符号数相加,运算结果用5bit存放足够。

如果两个数被限制在一个小的范围,则运算结果也可以附给一个位宽比较小的变量。

例如:两个4bit无符号数,其范围被限定为(0,3),则运算结果范围是(0,6),用一个3bit的变量就够了。

因此,在计算过程中,可根据变量的取值范围灵活选用位宽,这也是最根本的原则。无论什么类型的数相加或者相减,都可以根据计算结果的范围来确定变量的位宽。

例如:两个4bit数相减,计算结果的范围是(-15,15)。因此使用5bit数就保证不会溢出。

Verilog语法要求,若无符号数与有符号数相加减,计算过程会将将两个数当成无符号数处理。因此写RTL时,要保证两个数的类型一样。

例如:4bit有符号数1010(-6)与4bit无符号数1010(10)相加。

若当成两个无符号数相加,运算结果是1 0100。对于这个结果,当成无符号是20,当成有符号数是-12。都不对。

因此,首先需要将4bit无符号数扩成5bit有符号数,1010-> 0 1010。与4bit1010 相加的结果是一个6bit有符号数。

00 1010(10) + 11 1010(-6) = 00 0100(4)

Verilog可写成:

wire [3:0] a, b; //把a当成有符号数,b当成无符号数

wire [5:0] sum;

assign sum = {2{a[3]},a} + {2'b0, b}; //这里主动对a,b进行扩位

也可以写成:

wire [3:0] signed a;

wire [3:0] b;

wire [5:0] signed sum;

assign sum = a + $signed({1'b0,b}); //这里让编译器对a,b进行扩位,需要用signed指定变量类型

总结:

(1)保证类型一致;

(2)计算结果范围决定结果位宽;

(3)所有变量位宽扩成一致(手动扩,或者编译器自动扩)。

乘法

有了加法基础,乘法也好理解。在RTL使用乘法时,与加法考虑的一样:

(1)保证两个因子类型一致;

(2)计算结果范围决定结果位宽;

(3)把扩位交给编译器做。

通常情况下,无论是有符号数相乘还是无符号数相乘,位宽a与位宽b的因子相乘,结果位宽是a+b;

举例(1):无符号数相乘

1010 * 1010 = 0000 1010 * 0000 1010 = 0110 0100(100)

assign c = a * b

(2):有符号相乘

1010(-6) * 1010(-6) = 1111 1010 * 1111 1010 = 0010 0100 (36)

assign c = $signed(a ) * $signed(b);

(3)有符号与无符号相乘

1010 (-6)* 1010(10)= 1 1111 1010 * 0 0000 1010 = 1 1100 0001(-60)

assign c= $signed(a) * $signed({b[3], b});

Guess you like

Origin blog.csdn.net/zhong_ethan/article/details/119854847