【FPGA开发】Verilog-数据截断时实现四舍五入效果
目录
- 实现目标
- 直接截断低位
- 考虑四舍五入
实现目标
由于FPGA以定点数运算为主,随着数字信号处理的流程增加,数据位宽会逐渐变大,有时,考虑到资源量问题,会对定点数进行截断处理。
对于定点数来说,低位表示小数部分,截断低位,意味着抛弃小数,也就是损失精度。
假设一乘法器的运算结果是32-bits,后续要对该结果进行滤波处理,由于32-bits过大,需要对齐进行截断处理。
直接截断低位
直接截断低位是最简单的办法,相当于FLOOR()函数。
考虑四舍五入
以32-bits截断成17-bits为例。
想保留的是[31:15]这部分数据,如果直接截断,直接舍弃[14:0]即可,也就是说,把[14:0]这部分看成了小数。
所以在理解定点数的四舍五入时,把[31:15]看做整数,把[14:0]看做小数,小数部分大于等于0.5,就进位1;小数部分不超过0.5,就舍弃。
得到下述代码:
module fixed_point_rounding (
input wire [31:0] in_data, // 假设输入是 32位定点数,小数点在第 15 位后
output reg [17:0] out_data // 输出是 17 位整数
);
always @(*) begin
// 判断要舍弃部分的最高位
if (in_data[14]) begin
// 如果该位为 1,向前进位
out_data = in_data[31:15] + 1;
end else begin
// 如果该位为 0,直接舍弃
out_data = in_data[31:15];
end
end
endmodule
再次基础上,考虑正数和负数在四舍五入时的区别。
1.3 四舍五入成 1
1.6 四舍五入成 2
-1.3 四舍五入成 -1
-1.6 四舍五入成 -2
正数向0的方向(数值变小的方向)舍,向数值变大的方向 入。
负数向0的方向(数值变大的方向)舍,向数值变小的方向 入。
这里的实现方法有很多,可以参考Verilog对数据进行四舍五入(round)与饱和(saturation)截位
这里再补充一种写法:
assign buf_data = in_data[31] ? in_data + 15'b011_1111_1111_1111 : in_data + 15'b100_0000_0000_0000 ;
assign out_data = buf_data[31:15];
考虑正数情况,把要截断的尾数看成小数,要截断15位, .100_0000_0000_0000 就是0.5,比这个大,就应该进位,所以正数情况要加 15’b100_0000_0000_0000,这样就能保证大于等于0.5的情况都能进位上去。
考虑负数情况,同样把要截断的尾数看成小数,要截断15位,.100_0000_0000_0000也是0.5,不过要注意的是,对于负数的补码来说,符号位的权值是负数((10.10) = (-2+0.5=-1.5)),对于负数来说,-1.5四舍五入后是要变成-2的,如果-1.5的小数位要是进位给整数位,会变成-1,这与实际不符合!所以,要保证.100_0000_0000_0000正好不产生进位,也就是要加15’b011_1111_1111_1111。