verilog数的表示和定点化

发布时间 2023-07-15 11:44:21作者: luckylan

1.数的表示

1.1 数制转换

十进制整数转换成其他进制数:“除基取余”:十进制整数不断除以转换进制基数,直至商为0。每除一次取一个余数,从低位排向高位。

十进制小数转换成其他进制数:乘基取整,直至ε,高位到低位;“乘基取整”:用转换进制的基数乘以小数部分,直至小数为0或达到转换精度要求的位数。每乘一次取一次整数,从最高位排到最低位。

若要转换的数既有整数又有小数时,整数、小数分别转换。

 

1.2 二-十进制码(BCD码)

用4位二进制编码表示十进制的0-9十个数码。对于一个多位的十进制数,需要有与十进制位数相同的几组BCD代码来表示。

 

1.3 ASCII码

美国标准信息交换码,采用7位二进制表示27=128个包括0-9,字母等可打印字符。

 

数字0-9的ASCII码范围为:011_0000-011_1001:48~57

大写字母A-Z的ASCII码范围为:100_0001-101_1010:65~90

小写字母a-z的ASCII码范围为:110_0001-111_1001:97~122

1.4 有符号数和无符号数

有符号数指的就是带有符号位的数据,其中最高位就是符号位(如果最高位为0,那么表示是正数,如果最高位为1,那么表示是负数);无符号数就是不带有符号位的数据。

考虑一个4位的整数4’b1011:

如果它是一个无符号数据,那么它表示的值为:$ 1\times 2^{3}+0\times 2^{2}+1\times 2^{1}+1\times 2^{0}=11$

如果它是一个有符号数,那么它表示的值为:−1∗23+0∗22+1∗21+1∗20=−5

所以相同的二进制数把它定义为有符号数和无符号数表示的数值大小有可能是不同的。有符号数和无符号数转化为10进制表示的时候唯一的区别就是最高位的权重不同,由上例知,无符号数最高位的权重是 ,而有符号数最高位的权重是−23 。

正因为有符号数和无符号数最高位的权重不同,所以他们所表示的数据范围也是不同的。比如,一个4位的无符号整数的数据范围为0~15,分别对应二进制4’b0000~4’b1111,而一个4位的有符号整数的数据范围为-8~7,分别对应二进制4’b1000~4’b0111.

扩展到一般情况,一个位宽为M的无符号整数的数据范围为0∽2�−1,而一个位宽为M的有符号整数的数据范围为−2�−1∽2�−1−1。

N位有符号正数X的补码与该正数的原码相同,如4位有符号数0011的补码还是0011,对应无符号数3.

N位负数X的补码所表示的无符号数对应2�−|�| ,如4位有符号数1011(-3)的补码是1101,对应无符号数13=24−|−3|.

1.4.1 有符号数

数据的量化以及截位处理中,一种比较精确的处理方式就是先对截位后的数据进行四舍五入(round),如果在四舍五入的过程中由于进位导致数据溢出,那么我们一般会对信号做饱和(saturation)处理。

饱和处理就是如果计算结果超出了要求的数据格式能存储的数据的最大值,那么就用最大值去表示这个数据,如果计算结果超出了要求的数据格式能存储的数据的最最小值,那么就用最小值去表示这个数据。

规定:如果一个有符号数的总位宽为32位(其中最高位为符号位),小数位宽为16位,那么这个有符号数的数据格式记为32Q16。依次类推,10Q8表示这个数是一个有符号数(最高位为符号位),且总位宽为10位,小数位宽为8位。16Q13表示这个数是一个有符号数(最高位为符号位),且总位宽为16位,小数位宽为13位。总而言之,如果定义一个数据为mQn(m和n均为正整数且m>n)格式,那么我们可以得到三个重要的信息:

  1、mQn是一个有符号数,最高位为符号位

  2、mQn数据的总位宽为m

  3、mQn数据的小数位宽为n

1.4.2 有符号整数的符号位扩展

如果把一个4位的有符号整数扩展成6位的有符号整数。假设一个4位的有符号整数为4’b0101,显然由于最高位为0,所以它是一个正数,如果要把它扩展成6位,那么只需要在最前面加2个0即可,扩展之后的结果为:6’b000101。

假设一个4位的有符号整数为4’b1011,显然由于最高位为1,所以它是一个负数,如果要把它扩展成6位,,前面不是添2个0,而是添2个1,扩展之后的结果为:6’b111011。为了确保数据扩位以后没有发生错误,这里做一个简单的验证:

4′�1011=−1∗23+0∗22+1∗21+1∗20=−8+0+2+1=−5

6′�111011=−1∗25+1∗24+1∗23+0∗22+1∗21+1∗20=−32+16+8+2+1=−5

 

显然扩位以后数据大小并未发生变化。综上:对一个有符号整数进行扩位的时候为了保证数据大小不发生变化,扩位的时候应该添加的是符号位。

1.4.3 有符号小数

 接下来研究一下有符号小数。前面已经规定了有符号小数的记法。

假设一个有符号小数为4’b1011,它的数据格式为4Q2,也就是说它的小数位为2位。那么看看这个数表示的10进制数是多少

   4′�10.11=−1∗21+0∗20+1∗2−1+1∗2−2=−2+0+0.5+0.25=−1.25

显然,小数的计算方法实际上和整数的计算方法是一样的,只不过我们要根据小数点的位置来确定对应的权重。

 下面看看有符号小数的数据范围。拿4Q2格式的数据来说,它的数据范围为−2∽2−2−2,对应二进制4’b1000~4’b0111.扩展到一般情况,mQn格式的数据范围为:−2�−�−1∽2�−�−1+1−2−�。

最后再来看看有符号小数的数据扩展。假设一个有符号小数为4’b1011,它的数据格式为4Q2,现在要把这个数据用6Q3格式的数据存储。显然需要把整数部分和小数部分分别扩一位,整数部分采用上一节提到的符号位扩展,小数部分则在最后面添一个0,扩展以后的结果为6’b110110,接下来仍然做一个验证:

4′�10.11=−1∗21+0∗20+1∗2−1+1∗2−2=−2+0+0.5+0.25=−1.25

6′�110.110=−1∗22+1∗21+0∗20+1∗2−1+1∗2−2+0∗2−3=−4+2+0+0.5+0.25+0=−1.25

 显然,扩位以后数据大小并未发生变化。

 总结:有符号小数进行扩位时整数部分进行符号位扩展,小数部分在末尾添0.

1.4.4 两个有符号数的和

两个有符号数相加,为了保证和不溢出,首先应该把两个数据进行扩展使小数点对齐,然后把扩展后的数据继续进行一位的符号位扩展,这样相加的结果才能保证不溢出。

现在把5Q2的数据5’b100.01和4Q3的数据4’b1.011相加。

Step1、由于5Q2的数据小数位只有2位,而4Q3的数据小数点有3位,所以先把5Q2的数据5’b100.01扩位为6Q3的数据6’b100.010,使它和4Q3数据的小数点对齐

Step2、小数点对齐以后,然后把4Q3的数据4’b1.011进行符号位扩展成6Q3的数据6’b111.011

Step3、两个6Q3的数据相加,为了保证和不溢出,和应该用7Q3的数据来存储。所以需要先把两个6Q3的数据进行符号位扩展成7Q3的数据,然后相加,这样才能保证计算结果是完全正确的。

以上就是两个有符号数据相加需要做的一系列转化。下面思考为什么两个6Q3的数据相加必须用7Q3的数据才能准确的存储他们的和。 因为6Q3格式数据的数据范围为−4∽4−123 ;那么两个6Q3格式的数据相加和的范围为−8∽8−122;显然如果和仍然用6Q3来存一定会溢出,而7Q3格式数据的数据范围为−8∽8−123 ,因此用7Q3格式的数据来存2个6Q3格式数据的和一定不会溢出。

结论:在用Verilog做加法运算时,两个加数一定要对齐小数点并做符号位扩展以后相加,和才能保证不溢出。

1.4.5 两个有符号数的积

两个有符号数相乘,为了保证积不溢出,积的总数据位宽为两个有符号数的总位宽之和,积的小数数据位宽为两个有符号数的小数位宽之和。简单来说,两个4Q2数据相乘,要想保证积不溢出,积应该用8Q4格式来存。这是因为4Q2格式数据的范围为:−2∽2−2−2,那么两个4Q2数据相乘积的范围为:−4+1/2∽4,而8Q4格式的数据范围为:−8∽8−124,一定能准确的存放两个4Q2格式数据的积。

结论: mQn和aQb数据相乘,积应该用(m+a)Q(n+b)格式的数据进行存储。

1.4.6 有符号数乘法

例如有符号数[3:0]a * [3:0]b. 其中a=-5,b=7。a用补码表示为1011,b用补码表示是0111,对于这个例子,乘法过程如下:

其中,b的符号位跟a相乘的时候需要注意,如果b的符号位是1,则b的符号位与a相乘的时候实际表示的是-1*a,所以需要将a的结果按位取反加一取反加一。上面的例子b的符号位是0,所以结果是1101_1101,补码是1100011 = -35。如果遇到符号位是1的情况,比如上面的右图a=-5,b=-3,可以看到上面最后一行的结果需要对a进行取反加一取反加一才正确,并且此时取反加一也包括a的符号位。

另外,还需要注意的是所有部分积都要补符号位补到乘法输出值的位数。

其实乘法器就是由加法组成,所以b中的每一位跟a做乘法(异或)之后把部分积累加时,仍然需要遵从加法的原则,扩展符号位直到达到输出位宽,然后再加。

所以有符号乘法跟无符号乘法的区别就在这,无符号乘法不需要考虑符号位扩展问题,而有符号乘法在累加部分积的时候需要做符号位扩展,并且还要考虑符号位参与乘法时的含义不同,也就是说符号位的0表示0,但1却表示-1,所以符号位的1做乘法就不是异或而是对所有位取反再加一了。

在verilog中,一般有符号乘法器的做法是先将补码输入都转成原码,再将符号位单独拿出来进行异或,然后其余部分当作无符号数乘起来,最后再对结果取补码转回原码结果。

1.4.7 Verilog-2001有符号运算

在Verilog-1995中,integer数据类型为有符号类型,而reg和wire类型为无符号类型。而且integer大小固定,即为32位数据。在Verilog-2001中对符号运算进行了如下扩展。
Reg和wire变量可以定义为有符号类型:

reg signed [63:0] data;
wire signed [7:0] vector;
input signed [31:0] a;
function signed [128:0] alu;

函数返回类型可以定义为有符号类型。
带有基数的整数也可以定义为有符号数,在基数符号前加入s符号。

16'hC501 //an unsigned 16-bit hex value
16'shC501 //a signed 16-bit hex value

操作数可以在无符号和有符号之间转变。通过系统函数signed和unsigned实现。

reg [63:0] a; //unsigned data type
always @(a) begin
	result1 = a / 2; //unsigned arithmetic
	result2 = $signed(a) / 2;//signed arithmetic
end

1.4.8 注意事项

当想要进行有符号乘法时,我们想通过定义成signed让综合器选择有符号乘法器,此时我们需要把乘法器的两个输入和一个输出都定义成signed,哪怕是一个定义成signed,另一个用补码但未定义signed也不行。也可以把input port定义成signed。

1.4.9 数据传递

将一个数或者计算结果A(有符号数或者无符号)赋值给另一个数B时,根据位宽不同有以下三种情况:

1.当A和B位宽相同的时候的直接赋值:无论B是有符号或者无符号结果,只是简单的将A各个二进制位上的0或者1完全不变的赋值给B对应的位,并不会传递这个数是整数还是负数,是补码还是原码,但如果定义了B为signed ,则赋值给B后,就按照补码存储,符号位为1为负数,否则为正数,但如果未定义signed,则默认按照无符号数值结果存储。

2.对于长位宽直接赋值给短位宽即A的位宽大于B的位宽的A赋值于B的情况:无论左操作数B、右操作数A是有符号数还是无符号数,都是直接截断高位,而左操作数B二进制所表示的实际十进制数据要看左操作数B是无符号数还是有符号数,如果左操作数是无符号数unsigned,直接转换成十进制即可,如果是有符号数,则看成2的补码转换成十进制数。

3.对于短位宽赋值给长位宽的情况,需要对高位进行位扩展,具体是扩展1还是扩展0,记住:完全依据右操作数A,具体如下:

1)右操作数是无符号数,则无论左操作数是什么类型,高位都扩展成0;

2)右操作数是有符号数,则要看右操作数的符号位,按照右操作数的符号位扩展,符号位是1就扩展1,是0就扩展0;

3)位扩展后的左操作按照是无符号数还是有符号数解释成对应的十进制数值,如果是无符号数,则直接转换成十进制数值,如果是有符号数,则看成2的补码转换成十进制数;

4)从上面4种情况看出,有符号数赋值成无符号数会出现数据错误的情况,因此要避免这种赋值,而其他情况都是可以保证数据正确的。

回到顶部

1.5 浮点表示

定点运算有两个缺点:①可处理动态范围小;②由截尾舍入产生的百分比误差随着数的绝对值的减小而增加,这个问题可利用浮点数来解决。根据IEE754-1985标准,非负数n可以用两个参数表示,即尾数M和指数E,其表示形式为:�=�×2�

sign

exponent

significand

符号S

指数E

尾数

指数 exponent的位宽决定了可以表示数据的范围,尾数的位宽决定了可以表示数据的精度

假设:浮点数整体位宽是14bits,指数 exponent位宽5bits,尾数Significand位宽8bits.

用14bit的浮点表示十进制数32:32=1.0∗25=0.1∗26,(0.1为2进制表示的0.1).

所以指数exponent=110,尾数1000_0000,符号位sign=0,所以32=0_00110_1000_0000,

这种表示方法的另一个问题是由于 exponent没有符号位,没法表示负指数(比如 )

为了解决编码浪费空间问题,可以规定significand的第一位必须是1,这一过程称作归一化( normalization),所有 significands以0.1xxxxx形式表示,例如4.5=100.1∗20=0.1001∗23

从而使得尾数M限制为在[0.5,1]范围内的二进制小数, significand的第一位必须是1.

为了使 exponents可以表示负指数,采用 biased exponent方法。

令偏置数据( bias number)是一个根据 exponent位宽而定的居中的数(5位表示最大为25 ,居中的数是16=24 ),负指数加上这个偏置数据后可以变为正数表达。从而得到指数E,为一个正的或负的二进制整数。

0.0625(10)=1.0∗2−4=0.1∗2−3, bias exponent=16,故在-3指数上加16,得到新的指数13(10)=01101(2)

0.0625(10)=0_01101_10000000

-26.625(10): 26.625(10)=11010.101(2)=0.11010101∗25 ,在指数5上加 bias number16,得到最终指数项21(10)=10101(2)  -26.625(10)=1_10101_11010101

1.5.1 IEEE浮点数标准

在IEEE标准中,significant的表示方法是:1.xxx….,例如4.5=0.1001∗23 ,在IEEE标准中就应该表示为1.001∗22 ,尾数的第一位1是隐含的,也就是说这个1不会出现在significant编码域,故significant编码中只包含001.

单精度浮点位宽32bit,其中指数E部分具有8位 (bias127),尾数部分则具有23位,还有一位用做符号位S(0正,1负)。

指数部分有偏编码为E-127。E的范围为:-126~127, 2128=3.4×1038;

双精度浮点位宽64bit,其中指数E部分具有11位(bias1023),尾数部分则具有52位,还有一位用做符号位S(0正,1负)。

尾数域的最高有效位总是1,由此,该标准约定这一位不予存储,而是认为隐藏在小数点的左边,因此,尾数域所表示的值是1.M(实际存储的是M)