【转载】Verilog对数据进行四舍五入(round)与饱和(saturation)截位
Verilog对数据进行四舍五入(round)与饱和(saturation)截位https://www.cnblogs.com/liujinggang/p/10549095.html
一、软件平台与硬件平台
软件平台:
操作系统:Windows 8.1 64-bit
开发套件:Vivado2015.4.2 Matlab2016a
仿真工具:Vivado自带仿真器
二、引言
在利用Verilog写数字信号处理相关算法的过程中往往涉及到对数据的量化以及截位处理。而在实际项目中,一种比较精确的处理方式就是先对截位后的数据进行四舍五入(round),如果在四舍五入的过程中由于进位导致数据溢出,那么我们一般会对信号做饱和(saturation)处理。所谓饱和处理就是如果计算结果超出了要求的数据格式能存储的数据的最大值,那么就用最大值去表示这个数据,如果计算结果超出了要求的数据格式能存储的数据的最最小值,那么就用最小值去表示这个数据。这里先不给例子,下文会详细描述这种情况。
为了叙述方便,本文先做如下规定:如果一个有符号数的总位宽为32位(其中最高位为符号位),小数位宽为16位,那么这个有符号数的数据格式记为32Q16。依次类推,10Q8表示这个数是一个有符号数(最高位为符号位),且总位宽为10位,小数位宽为8位。16Q13表示这个数是一个有符号数(最高位为符号位),且总位宽为16位,小数位宽为13位。总而言之,下文如果定义一个数据为mQn(m和n均为正整数且m>n)格式,那么我们可以得到三个重要的信息:
1、mQn是一个有符号数,最高位为符号位
2、mQn数据的总位宽为m
3、mQn数据的小数位宽为n
三、Verilog中有符号数据的补位与截位
3.1 有符号数与无符号数
顾名思义,有符号数指的就是带有符号位的数据,其中最高位就是符号位(如果最高位为0,那么表示是正数,如果最高位为1,那么表示是负数);无符号数就是不带有符号位的数据。
考虑一个4位的整数4’b1011.如果它是一个无符号数据,那么它表示的值为:1*23+0*22+1*21+1*20 = 11.如果它是一个有符号数,那么它表示的值为:1*(-23)+0*22+1*21+1*20 = -5.所以相同的二进制数把它定义为有符号数和无符号数表示的数值大小有可能是不同的。同时,这里也告诉大家,有符号数和无符号数转化为10进制表示的时候唯一的区别就是最高位的权重不同,拿上例来说,无符号数最高位的权重是23而有符号数最高位的权重是-23。
正因为有符号数和无符号数最高位的权重不同,所以他们所表示的数据范围也是不同的。比如,一个4位的无符号整数的数据范围为0~15,分别对应二进制4’b0000~4’b1111,而一个4位的有符号整数的数据范围为-8~7,分别对应二进制4’b1000~4’b0111.
扩展到一般情况,一个位宽为m的无符号整数的数据范围为0~2m-1,而一个位宽为m的有符号整数的数据范围为-2(m-1)~2(m-1)-1。
3.2 有符号整数的符号位扩展
问题:如何把一个4位的有符号整数扩展成6位的有符号整数。
假设一个4位的有符号整数为4’b0101,显然由于最高位为0,所以它是一个正数,如果要把它扩展成6位,那么只需要在最前面加2个0即可,扩展之后的结果为:6’b000101。
在看另外一个例子,假设一个4位的有符号整数为4’b1011,显然由于最高位为1,所以它是一个负数,如果要把它扩展成6位,那么这里要千万注意了,前面不是添2个0,而是添2个1,扩展之后的结果为:6’b111011。为了确保数据扩位以后没有发生错误,这里做一个简单的验证:
4’b1011 = 1*(-23)+0*22+1*21+1*20 = -8 + 0 + 2 + 1 = -5
6’b111011 = 1*(-25)+1*24+1*23+0*22+1*21+1*20 = -32+16+8+2+1=-5
显然扩位以后数据大小并未发生变化。
综上得出结论:对一个有符号整数进行扩位的时候为了保证数据大小不发生变化,扩位的时候应该添加的是符号位。
3.3 有符号小数
有了前面两小节的基础以后接下来研究一下有符号小数。前面已经规定了有符号小数的记法。
假设一个有符号小数为4’b1011,它的数据格式为4Q2,也就是说它的小数位为2位。那么看看这个数表示的10进制数是多少
4’b10.11 = 1*(-21)+0*20+1*2-1+1*2-2 = -2 + 0 + 0.5 + 0.25 = -1.25
显然,小数的计算方法实际上和整数的计算方法是一样的,只不过我们要根据小数点的位置来确定对应的权重。
接下来看看有符号小数的数据范围。就拿4Q2格式的数据来说,它的数据范围为-2~(2-1/22),分别对应二进制4’b1000~4’b0111.扩展到一般情况,mQn格式数据的数据范围为-2(m-n-1)~2(m-n-1)-1/2n。
最后再来看看有符号小数的数据扩展。假设一个有符号小数为4’b1011,它的数据格式为4Q2,现在要把这个数据用6Q3格式的数据存储。显然需要把整数部分和小数部分分别扩一位,整数部分采用上一节提到的符号位扩展,小数部分则在最后面添一个0,扩展以后的结果为6’b110110,接下来仍然做一个验证
4’b10.11 = 1*(-21)+0*20+1*2-1+1*2-2 = -2 + 0 + 0.5 + 0.25 = -1.25
6’b110.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.
3.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-1/23;那么两个6Q3格式的数据相加和的范围为-8~8-1/22;显然如果和仍然用6Q3来存一定会溢出,而7Q3格式数据的数据范围为-8~8-1/23,因此用7Q3格式的数据来存2个6Q3格式数据的和一定不会溢出。
结论:在用Verilog做加法运算时,两个加数一定要对齐小数点并做符号位扩展以后相加,和才能保证不溢出。
3.5 两个有符号数的积
两个有符号数相乘,为了保证积不溢出,积的总数据位宽为两个有符号数的总位宽之和,积的小数数据位宽为两个有符号数的小数位宽之和。简单来说,两个4Q2数据相乘,要想保证积不溢出,积应该用8Q4格式来存。这是因为4Q2格式数据的范围为:-2~(2-1/22),那么两个4Q2数据相乘积的范围为:(-4+1/2)~4,而8Q4格式的数据范围为:-8~(8-1/24),一定能准确的存放两个4Q2格式数据的积。
结论: mQn和aQb数据相乘,积应该用(m+a)Q(n+b)格式的数据进行存储。
3.6 四舍五入(round)
前面讲的都是对数据进行扩位,这一节说的是对数据截位时如何进行四舍五入以提高截位后数据的精度。
假设一个9Q6格式的数据为:9’b011.101101,现在只想保留3位小数位,显然必须把最后三位小数位截掉,但是不能直接把数据截成6’b011.101,这样是不精确的,工程上一般也不允许这么做,正确的做法是先看这个数据是正数还是负数,因为9’b011.101101的最高位为0,所以它是一个正数,然后再看截掉部分(此例中截掉部分是最末尾的101)的最高位是0还是1,在数据是正数的情况下,如果截掉部分的最高位为1,那么是需要产生进位的,所以,最终9’b011.101101应该被截成6’b011.110.
如果是负数则正好相反。假设一个9Q6格式的数据为:9’b100.101101,由于最高位是1,所以这个数是一个负数,然后再看截断部分的最高位以及除最高位的其他位是否有1,此例中截断部分(截断部分为末尾的101)的最高位为1,而且除最高位以外的其他位也有为1的情况,由于负数最高位的权重是(-22),所以对于这种情况是不需要进位的,与正数不同的是,负数不进位是需要加1的。因此最终9’b100.101101应该被截成6’b100.110。
假设a是一个9Q6格式的数据,要求把小数位截成3位。下面是Verilog代码:
assign carry_bit = a ? ( a & ( |a ) ) : a ;
assign a_round = {a, a} + carry_bit ;
上面的代码第一行是通过判断符号位a和截断部分数据特征来确定是否需要进位,如果a是0,计算得到的carry_bit为1,则表示是a是正数,且截断是需要进位;如果a是1,计算得到的carry_bit为1,则表示是a是负数,且截断是不需要进位的,负数不进位需要加1。代码第二行为了保证进位后数据不溢出,所以扩展了一位符号位。
3.7 饱和(saturation)截位
所谓饱和处理就是如果计算结果超出了要求的数据格式能存储的数据的最大值,那么就用最大值去表示这个数据,如果计算结果超出了要求的数据格式能存储的数据的最最小值,那么就用最小值去表示这个数据。
例1:有一个6Q3的数据为6’b011.111,现在要求用4Q2格式的数据去存储它,显然6’b011.111转化为10进制如下:
6’b011.111 = 1*21+1*20+1*2-1+1*2-2+1*2-3 = 3.875
而4Q2格式的数据能表示的数据的最大值为4’b01.11,转化为10进制为1.75,因此4Q2格式的数据根本无法准确的存放3.875这个数据,这样就是所谓的饱和情况。在这种情况下,饱和处理就是把超过了1.75的所有数据全部用1.75来表示,也就是说,6Q3的数据为6’b011.111如果非要用4Q2格式的数据来存储的话,在进行饱和处理的情况下最终的存储结果为:4’b01.11。
例2:有一个6Q3的数据为6’b100.111,现在要求用4Q2格式的数据去存储它,显然6’b100.111转化为10进制如下:
6’b100.111 = 1*(-22)+1*2-1+1*2-2+1*2-3 = -4 + 0.5 + 0.25 + 0.125 = -3.125
而4Q2格式的数据能表示的数据的最小值为4’b10.00,转化为10进制为-2,因此4Q2格式的数据根本无法准确的存放-3.125这个数据,这是另一种饱和情况。在这种情况下,饱和处理就是把小于-2的所有数据全部用-2来表示,也就是说,6Q3的数据为6’b100.111如果非要用4Q2格式的数据来存储的话,在进行饱和处理的情况下最终的存储结果为:4’b10.00。
四、实例演示(a + b * c)
4.1 题目要求
假设a的数据格式为16Q14,b的数据格式为16Q14,c的数据格式为16Q15,请计算s=a+b*c的值,其中s的数据格式为16Q14。在截位的过程中利用四舍五入(round)的方式保证数据精度,如果有数据溢出的情况,请用饱和截位的方式进行处理。编写完Verilog代码以后利用Matlab产生a、b、c的数据对写的Verilog代码进行仿真,并保证Matlab运算得到的数据和Verilog运算得到的数据全部相同。最后,有条件的利用VCS统计代码覆盖率(Code Coverage),确保条件覆盖率(Condition Coverage)达到100%。
4.2 要求分析
1、先分析b*c。由于b的数据格式为16Q14,c的数据格式为16Q15,所以为了保证b*c的乘积不溢出,那么b*c的乘积的数据格式为(16+16)Q(14+15),即32Q29。
2、再分析加法。由于a的数据格式为16Q14,而b*c的积的数据格式为32Q29,所以相加之前要先把a扩展成32Q29格式的数据,又为了保证相加的结果不溢出,相加之前还要把两个32Q29格式的数据进行1位符号位扩展成33Q29格式的数据以后再相加,相加得到的和的数据格式为33Q29.
3、最后,由于要求最终的结果为16Q14,所以需要把33Q29的数据截位为16Q14,如果出现数据溢出的情况,需要用饱和截位的方式进行处理。
分析完毕以后,就开始进行代码的编写。
4.3 Verilog代码分析
本题的Verilog代码如下
1 module dsp( 2 input I_clk , 3 input I_rst_n , 4 inputsigned I_a , // 16Q14 5 inputsigned I_b , // 16Q14 6 inputsigned I_c , // 16Q15 7 output signed O_s // 16Q14 8 ); 9 10 reg signed R_a_16Q14 ;// 16Q1411 reg signed R_b_16Q14 ;// 16Q1412 reg signed R_c_16Q15 ;// 16Q1513 14 wire signed W_mult_b_c_32Q29 ; //32Q29 15 wire signed W_s_33Q29 ; // 33Q2916 wire signed W_a_32Q29 ;17 18 wire W_carry_bit ;19 wire W_s_19Q14_round ;20 21 always @(posedge I_clk or negedge I_rst_n)22 begin23 if(!I_rst_n)24 begin25 R_a_16Q14
页:
[1]