it编程 > 硬件开发 > fpga开发

基于FPGA的数字信号处理(15)--定点数的舍入模式(6)向0取整fix

59人参与 2024-08-03 fpga开发

目录

1、前言

2、10进制数的fix

3、2进制数的fix


        文章总目录点这里:《基于fpga的数字信号处理》专栏的导航与说明


1、前言

        在之前的文章介绍了定点数为什么需要舍入和几种常见的舍入模式。今天我们再来看看另外一种舍入模式:向上取整fix。

2、10进制数的fix

        fix:也叫 向0取整。它的舍入方式是数据往0的方向,舍入到最近的整数,比如1.75 fix到2,-0.25 fix到0等。以-2到1.75之间的16个数据(步长0.25)为例,它们的 fix 结果是这样的:

% 打印数据

        从上图可以看到:

3、2进制数的fix

        2进制数的fix和10进制的fix类似,但是对于负数部分的处理是不同的。以q4.2格式的定点数(字长4位,小数2位的有符号数)为例,对于负数的小数部分的处理:

        总结一下,就是:

        对于正数和0的处理和10进制的方式相同,都是:

        从上面可以看出来,fix对于正数来说相当于向下取整floor,对于负数来说相当于向上取整ceil。因此,fix的实现可以简化为:

image-20240421154458400

        下面以 用fix的方式来实现q4.2格式定点数转q2.0格式定点数为例,verilog代码如下:

module test(
    input   [3:0]   data_4q2,       //有符号数,符号1位,字长4位,小数2位   
    output  [1:0]   data_2q0        //有符号数,符号1位,字长2位,小数0位   
);
​
wire    carry;
​
assign  carry = data_4q2[3] && (|data_4q2[1:0]);    //是负数且非整数时进位为1,其他进位为0
assign  data_2q0 = data_4q2[3:2] + carry;           //舍弃低位(即整个小数部分)后再加进位
​
endmodule

        因为一共只有16个数,所以我们可以用穷举的方式来测试,tb如下:

`timescale 1ns/1ns
module test_tb();
​
reg  [3:0]  data_4q2;           //有符号数,符号1位,整数2位,小数2位   
wire [1:0]  data_2q0;           //有符号数,符号1位,整数2位,小数0位   
    
integer i;                      //循环变量
​
initial begin
    data_4q2 = 0;               //输入赋初值 
    for(i=0;i<16;i=i+1)begin    //遍历所有的输入,共16个  
        data_4q2 = i;                       
        #5; 
        $display("data_4q2:%h       data_2q0:%h",data_4q2,data_2q0);
    end
    #20 $stop();                //结束仿真
end
​
//例化被测试模块
test    test_inst(
    .data_4q2   (data_4q2), 
    .data_2q0   (data_2q0)
);
​
endmodule

        同时,我们也用matlab来实现同样的功能,观察两者的输出是否一致:

%--------------------------------------------------
% 关闭无关内容
clear;
close all;
clc;
​
%--------------------------------------------------
% 生成数据并做fix处理
x = -2:0.25:1.75;
f = fimath('roundingmethod','zero');        % 设定舍入模式为fix
data_4q2 = fi(x,1,4,2,f);                   % 生成q4.2格式的定点数
data_2q0 = fi(data_4q2,1,2,0,f);            % 从q4.2格式转换成q2.0格式
​
% 打印数据
for i=1:length(data_4q2)
    fprintf('data_4q2:%s    data_2q0:%s\n',hex(data_4q2(i)),hex(data_2q0(i)))
end

        下图是2者分别输出的数据(16进制),可以看到数据的输出是一致的,证明rtl代码无误。

image-20240421002442688

        这几个数的输入分别是0101/0110/0111,即10进制数1.25/1.5/1.75,它们fix结果应该是2。从上图来看,好像是matlab错了,而rtl对了,但实际情况恰恰相反。现在想想结果是什么格式的?q2.0!它能表示的最大的数是多少?是10进制的1!所以结果溢出了!

        那为什么rtl的结果又 ”对“ 了呢?这纯属是乌龙。因为打印结果是16进制的,并不表示10进制数值,结合结果的2位位宽,可知 ”2“,实际上就是10,它是01的溢出产生的,这个数在q2.0格式的定点数中并不表示 ”数字2“,而是数字 ”-1“。

        matlab是有溢出处理机制的(saturate),它把溢出值把都饱和在了最大值即01(10进制的1)。为了防止这种情况的发生,我们也要设计对应的溢出处理机制。因为是向上取整,所以结果只会是正向的溢出,那么就只要限定最大值即可,把verilog代码改一下:

module test(
    input   [3:0]   data_4q2,               //有符号数,符号1位,字长4位,小数2位   
    output  [1:0]   data_2q0                //有符号数,符号1位,字长2位,小数0位   
);
​
wire            carry;
wire    [2:0]   data_temp;                  //扩展1bit,防止溢出
​
assign  carry = |data_4q2[1:0];                                         //是整数时进位为0,非整数进位为1
assign  data_temp = {data_4q2[3],data_4q2[3:2]} + {2'b00,carry};        //中间变量,舍弃低位(即整个小数部分)后再加进位
assign  data_2q0 = (data_temp[2:1]==2'b01) ? 2'b01 : data_temp[1:0];    //data_2q0的高2位为01说明产生了正向的进位,即溢出
​
endmodule

        非整数的ceil,相当于先丢小数部分,然后把剩余的整数部分+1

image-20240420165316917

        定点数从q4.2格式转q2.0格式是一个比较特殊的例子,因为它相当于把小数部分全部舍弃掉了,如果舍入要求不是全部小数位,而是部分小数位,那么处理方式是一样的吗?

        是一样的。对于其他情况则只需要把精度要求外的小数部分舍弃即可。例如q5.3格式的定点数转q3.1格式,则只需要把最后两位小数舍弃即可,例如:

        其他类似,不赘述了。

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

FPGA:我的零基础学习路线(2022秋招已上岸)持续更新中~

08-03

FPGA零基础入门学习路线

08-03

VIVADO中FFT核的使用(FPGA计算FFT和IFFT)

08-03

【FPGA开发/IC开发之时序约束最全面的归纳总结】时序路径基本概念及时序约束分析方法

08-03

VMamba: Visual State Space Model论文笔记

08-03

数字IC面经汇总(32篇)

08-03

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论