【Verilog编程】定点数的数据饱和与截位

文章详细介绍了如何使用Verilog代码实现无符号数和有符号数的数据截位与饱和处理,包括四舍五入的截位算法和饱和算法的电路结构,并提供了相应的Verilog代码示例。讨论了不同数据类型的溢出范围和处理方法,以及正负数在截位和饱和时的特殊考虑。

在数字设计中,我们常常需要对数据进行量化和截位,考虑以下两种应用场景:
(1)如前一个模块输出10bit数据,后一个模块只能接收8bit数据,此时就需要我们进行截位处理。数据截位中比较精确的一种算法是对数据进行四舍五入
(2)计算单元完成计算后,输出16bit数据,但是整个模块输出数据为12bit,那么计算单元输出的数据很大可能会大于4095(无符号数12bit的最大值),此时就需要我们对计算单元输出的数据进行饱和处理。数据饱和是指:当数据大于我们所设定的最大值时,输出数据=最大值;当数据小于我们所设定的最小值时,输出数据=最小值

由于有符号数与无符号数所表示的数据大小和数据格式不相同,因此我们分开讨论这两种情况。为了方便叙述,先做如下约定:
1.有符号数表示为s(m,n)——有符号数,总位宽m,小数位宽为n
  如:s(10,0)表示有符号数,总位宽10,小数位宽0,即为一个负整数
2.无符号数表示为u(m,n)——无符号数,总位宽m,小数位宽n
  如:u(18,4)表示有符号数,总位宽18,小数位宽4

一、无符号数的饱和与截位

1.数据截位

当我们进行四舍五入截位时,需要考虑进位后数据是否溢出
假设现在有数据A格式为u(10,6),我们只需要保留三位小数得到数据B格式为u(7,3)。分以下两种情况:
1.假设A=1010.100101。我们分以下几步进行计算:
step1:数据B的总位宽为7bit,我们需要截掉数据A的低3bit,数据A的高7bit为1010.100,此时如果低3bit产生进位,高7bit不会溢出
step2:截掉数据的最高位bit[2]=1,需要产生进位。因为此时该bit位对数据B而言为B.1,Verilog中.1=.2^(-1)=0.5,所以需要进位。
step3:因此最终我们得到数据B=1010.100+1=1010.101
2.假设A=1111.111101.我们分以下几步进行计算:
step1:数据B的总位宽为7bit,我们需要截掉数据A的低3bit,数据A的高7bit为1111.111,此时如果低3bit产生进位,高7bit溢出!
step2:截掉数据的最高位bit[2]=1,需要产生进位。但是由于高7bit=1111.111为7bit所能表示的最大值,因此此处进位被丢掉。(此处同数据饱和处理)
step3:因此最终我们得到数据B=A[10:3]=1111.111

注:由于硬件存储的特性,小数存储以2^(-n)存储,其中n=1,2,…;因此如果被截掉的最高位≠1,那么此时被截掉的数据一定是小于0.5,不需要进位。因此在四舍五入的截位法中,被截掉的数据是否进位只需要考虑被截掉部分的最高位即可。

电路结构

根据上面两种情况,无符号数的数据截位算法过程总结如下:
输入数据为:data_in,位宽为:Din_W
输出数据为:data_out,位宽为:Dou_W
Step1:判断data_in[Din_W-1:Din_W-Dou-W]是否为全1——判断是否溢出
Step2:如果是全1,则data_out=data_in[Din_W-1:Din_W-Dou-W]
Step3:如果不是全1,则data_out=data_in[Din_W-1:Din_W-Dou-W]+data_in[Din_W-Dou-W-1]

无符号数的数据截位用电路可表示为
无符号数数据截位

Verilog代码

module  unsigned_data_rnd #(
    parameter   Din_W=8,
    parameter   Dou_W=4
)(
    input       [Din_W-1:0] data_in,
    output  reg [Dou_W-1:0] data_out
);

always  @(*)begin
    if(data_in[Din_W-1:Din_W-Dou_W]=={{Dou_W}{1'b1}})begin
        data_out=data_in[Din_W-1:Din_W-Dou_W];
    end
    else begin
        data_out=data_in[Din_W-1:Din_W-Dou_W]+data_in[Din_W-Dou_W-1];
    end
end
endmodule 

2.数据饱和

一个n位的无符号数,其表示的十进制范围为:0~(2^n-1),其最小值为0。因此无符号数的数据饱和只考虑最大值溢出
数据饱和讨论中,我们有以下两种场景:
1.假设现在有数据A格式为u(8,4),输出数据B格式为u(4,2)。
step1:首先我们将数据A通过数据截位得到数据C,格式为u(6,2)——对齐小数位
setp2:数据B能表示的最大值为4’b11.11,考虑数据C是否比数据B的最大值大
step3:如果数据C比数据B的最大值大,则输出数据B=4’b11.11(取最大值),如果数据C比数据B的最大值小,则输出数据B=C[3:0]
2.假设现在有数据A格式为u(8,4),输出数据B格式为u(6,5)。
step1:首先将在数据A的低位补0,扩展数据A的小数位,得到数据C,格式为u(9,5)——对齐小数位
step2:数据B能表示的最大值为6’b1.11111,考虑数据C是否比数据B的最大值大
step3:如果数据C比数据B的最大值大,则输出数据B=6’b1.11111(取最大值),如果数据C比数据B的最大值小,则输出数据B=C[5:0]

注:定点数的数据饱和中,第一步是对齐小数。通过对齐小数,可以简化我们数据饱和的过程。

电路结构

根据以上情况的分析,无符号数的数据饱和算法过程如下(假设小数位宽已经对齐):
输入数据为:data_in,位宽为:Din_W
输出数据为:data_out,位宽为:Dou_W
Step1:判断data_in[Din_W-1:Dou_W]是否为全0——判断数值大小
Step2:如果不是全0,则表示data_in大于data_out所能表示的最大值,则data_out={{Dou_W}{1’b1}}
Step3:如果是全0,则表示data_in小于data_out所能表示的最大值,则data_out=data_in[Dou_W-1:0]

无符号数的数据饱和用电路可表示为
无符号数的数据饱和

Verilog代码

module  unsigned_data_sat #(
    parameter   Din_W=8,
    parameter   Dou_W=4
)(
    input       [Din_W-1:0] data_in,
    output  reg [Dou_W-1:0] data_out
);

always  @(*)begin
    if(data_in[Din_W-1:Dou_W]=={{Din_W-Dou_W}{1'b0}})begin
        data_out=data_in[Dou_W-1:0];
    end
    else begin
        data_out={{Dou_W}{1'b1}};
    end
end
endmodule

二、有符号数的饱和与截位

1.数据截位

有符号数的数据截位分两种情况讨论:
(1)正数的数据截位,即最高位为0;同无符号数数据截位
(2)负数的数据截位,即最高位为1;
数学中,0.3四舍五入之后为0;0.5四舍五入之后为1;0.7四舍五入之后为1
同理,-0.3四舍五入之后为0;-0.5四舍五入之后为-1;-0.7四舍五入之后为-1
也就是说,数学中四舍五入和符号不相关,只与数字大小相关。在数字设计中,由于我们采用二进制补码形式存储数据,每个十进制数据都有唯一的表示,也就是说有符号数与无符号数的数据截位算法基本相同,但是有符号数的溢出范围和无符号数不同:
假设一个8bit无符号数整数,其表示范围为0(8’b0000_0000)~255(8’b1111_1111)
而一个8bit有符号数整数,其表示范围为-128(8’b1000_0000)~127(8’b0111_1111)
假设一个s(11,3),其值为-128.25(1000_0000110),截掉低3bit,其值为-127(1000_0001)
其值为-128.5(1000_0000100),截掉低3bit,其值为-128(1000_0001),因为-0.5不进位
其值为-128.75(1000_0000010),截掉低3bit,其值为-128(1000_0000),因为-0.75不进位
从上面可以看出,负数需要进位时,会从最下负值(-128)向上加(-128+1=-127),因此不会发生溢出。从上面的分析可以总结出,要被截掉的位Dcut
当Dcut>-0.5(即:-0.25的情况),最后得到的值为Dout=Din[Din_w-1:Din_w-Dcut_w]+Din[Dcut_w]
当Dcut=-0.5(即:-0.5的情况),最后得到的值为Dout=Din[Din_w-1:Din_w-Dcut_w]
当Dcut<-0.5(即:-0.75的情况),最后得到的值为Dout=Din[Din_w-1:Din_w-Dcut_w]+Din[Dcut_w](因为此时最高位为0,故可以如此操作)
因此当为负数时,只需要单独讨论-0.5的情况;当为正数时,用无符号数的截位

电路结构

因此,有符号数的数据截位算法过程总结如下:

输入数据为:data_in,位宽为:Din_W

输出数据为:data_out,位宽为:Dou_W

Step1:判断data_in[Din_W-1]为0还是1——判断正负

Step2:如果是1,则判断data_in[Din_W-Dou-W-1:0]是否为{1’b1,(Din_W-Dou-W-1){1’b0}}(被截掉部分的是否为0.5);

     如果是0,则判断data_in[Din_W-2:Din_W-Dou-W]是否为全1

Step3:当data_in为正数且data_in[Din_W-2:Din_W-Dou-W]为全1,则data_out=data_in[Din_W-1:Din_W-Dou-W]

     当data_in为负数且data_in[Din_W-Dou-W-1:0]={1’b1,(Din_W-Dou-W){1’b0}},则data_out=data_in[Din_W-1:Din_W-Dou_W]

Step4:如果Step3的条件均不满足,则data_out=data_in[Din_W-1:Din_W-Dou-W]+data_in[Din_W-Dou_W-1]

有符号数的数据截位用电路可表示为

在这里插入图片描述

Verilog代码

module  signed_data_rnd #(
    parameter   Din_W=8,
    parameter   Dou_W=4
)(
    input       [Din_W-1:0] data_in,
    output  reg [Dou_W-1:0] data_out
);

always  @(*)begin
    if((data_in[Din_W-1:Din_W-Dou_W]=={1'b0,{Dou_W-1}{1'b1}})||(data_in[Din_W-1]==1'b1&&data_in[Din_W-Dou_W-1:0]=={1'b1,(Din_w-Dou_w){1'b0}}))begin
        data_out=data_in[Din_W-1:Din_W-Dou_W];
    end
    else begin
        data_out=data_in[Din_W-1:Din_W-Dou_W]+data_in[Din_W-Dou_W-1];
    end
end
endmodule

2.数据饱和

注:根据无符号数的数据饱和计算公式,我们这里讨论的有符号数的数据饱和是基于输入数据和输出数据小数已经对齐的情况。

一个n位的有符号二进制,表示的范围是:-2^n~(2^n-1)。也就是说有符号数的数据饱和除了要考虑上溢的情况还需要考虑下溢情况。因此有符号数的数据饱和也分两部分考虑:
(1)数据上溢
    数据上溢基本与无符号数情况类似,但是需要主要的是,一个8bit无符号整数能表示的最大值为256;而一个8bit有符号整数表示的最大值为127。也就是说,我们判断上溢的条件会存在一bit的差别,如果我们需要饱和到数据结构为s(4,0),那么判断差别为:
有符号数数据上溢
(2)数据下溢
    数据下溢主要考虑负数情况,假设将数据A结构为s(8,0)饱和到数据B结构为s(4,0),数据B的最小值为-8(4’b1000),我们分别讨论以下几种情况:
有符号数的数据下溢

电路结构

根据以上讨论的两种情况,有符号数的数据饱和算法总结如下(假设小数位宽已对齐):
输入数据为:data_in,位宽为:Din_W
输出数据为:data_out,位宽为:Dou_W
Step1:判断data_in[Din_W-1]为0还是1——判断正负
Step2:如果为0,则判断data_in[Din_W-2:Dou_W-1]是否为全0;
     如果为1,则判断data_in[Din_W-2:Dou_W-1]是否为全1
Step3:如果data_in为正数且data_in[Din_W-2:Dou_W-1]不是全0,则data_out={1’b0,{Dou_W-1}{1’b1}}
     如果data_in为负数且data_in[Din_W-2:Dou_W-1]不是全1,则data_out={1’b1,{Dou_W-1}{1’b0}}
Step4:如果Step3的条件均不满足,则data_out=data_in[Dou_W-1:0]

有符号数的数据饱和用电路可表示为
有符号数的数据饱和

verilog代码

module  signed_data_sat #(
    parameter   Din_W=8,
    parameter   Dou_W=4
)(
    input       [Din_W-1:0] data_in,
    output  reg [Dou_W-1:0] data_out
);

always  @(*)begin
    if(data_in[Din_W-1]==1'b1&&data_in[Din_W-2:Dou_W-1]!={{Din_W-Dou_W}{1'b1}})begin
        data_out={1'b1,{{Dou_W-1}{1'b0}}};
    end
    else if(data_in[Din_W-1]==1'b0&&data_in[Din_W-2:Dou_W-1]!={{Din_W-Dou_W}{1'b1}})begin
        data_out={1'b0,{{Dou_W-1}{1'b1}}};
    end
    else begin
        data_out=data_in[Dou_W-1:0];
    end
end
endmodule

三、参考文献

Verilog对数据进行四舍五入(round)与饱和(saturation)截位

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

er橙汁儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值