Floatint-point 的使用
xilinx的常用ip应用系列(零)
ip使用分享(一)
- 设计平台:Vivado-2017.4(ISE或许也有相对应的)
最近用到了这样一个ip,给功能上是要将浮点数转换成定点数,那顺便我们就可以介绍一下Float-point这个大ip的使用方法.
例化和使用
顶栏 Window->IP Catalog->搜索’float’即得进入页面
官方手册 pg060
可见,xilinx将几乎所有得浮点数操作都封到这个ip上面了,包括了诸如加减乘除,平方开方等操作.
但需要注意的是,跟ise中的使用不一样,这里的数据输入都是经过axi接口的,所以上图中的S_AXIS_A即为输入数据项.
点击下一步后,就要选择输入数据类型了,有常见的半精度(Half),单精度(float)和双精度(double),当然还可以自定义整数和小数的位宽.这里以double为例.
同样,也要选择输出的定点数的范围,为了更好的演示效果,这里取custom,然后取1位符号位,31位整数位和32位的小数位.后面两步是设置延时和接口的,这里就不作介绍了.
简单写个例化代码
module float2point(
input CLK_50M,aresetn,
input [63:0] double_data,
output wire issigned,
output wire [30:0] zhengshu,
output wire [31:0] xiaoshu
);
wire m_axis_result_tvalid;
float2fix u_float2fix (
.aclk(CLK_50M), // input wire aclk
.s_axis_a_tvalid(1'b1), // input wire s_axis_a_tvalid
.aresetn(aresetn), // input wire aresetn
.s_axis_a_tdata(double_data), // input wire [63 : 0] s_axis_a_tdata
.m_axis_result_tvalid(m_axis_result_tvalid), // output wire m_axis_result_tvalid
.m_axis_result_tdata({issigned, zhengshu, xiaoshu}) // output wire [63 : 0] m_axis_result_tdata
);
endmodule
再简单地写个tb(其实也是自动生成的):
module f2p_tb();
localparam PERIOD = 20;
// float2point Inputs
reg CLK_50M=0;
reg [63:0] double_data=0;
reg aresetn = 1;
// float2point Outputs
wire issigned;
wire [30:0] zhengshu;
wire [31:0] xiaoshu;
float2point u_float2point (
.CLK_50M ( CLK_50M ),
.aresetn(aresetn),
.double_data ( double_data ),
.issigned ( issigned ),
.zhengshu ( zhengshu ),
.xiaoshu ( xiaoshu )
);
always #(PERIOD/2) CLK_50M = ~CLK_50M;
initial
begin
#(5*PERIOD) aresetn = 0;
#(5*PERIOD) aresetn = 1;
#(10*PERIOD)
#PERIOD double_data = 64'b0011111111111011001100110011001100110011001100110011001100110011;
#(10*PERIOD)
$stop;
end
endmodule
产生测试数据
那么就会有热心网友想问,你上面的测试数据咋生成的呢?
那 就要介绍一个这个 python代码了:
import bitstring
f1 = bitstring.BitArray(float=1.7, length=64)
print(f1.bin)
这里的float是指浮点数,length是32的时候是"float"类型,64的时候是"double"类型
接下来便是 仿真了:
显然,整数部分已然是出来了,但是小数部分说是有点古怪,不应该是7吗?
但是要仔细想想,在double数据类型里面,小数部分有53位,咋转成定点数就剩下4位(0-9)了呢?
显然,这个值是相对 2的32次方为1的时候考虑的,就是说,如果我们将这个数除以2的32次方,为其真实小数值.
已然是很接近0.7了.
做点发散思考
那么假设这个值是一个秒数,我们要在硬件计算当前是多上微秒要咋处理呢?
在完全不考虑优化的前提下(设整数为z,小数为x):
u
s
=
(
z
+
x
2
32
)
∗
1000000
us =(z + \frac{x}{2^{32}})* 1000 000
us=(z+232x)∗1000000
显然,将小数部分拆开:
u s = z ∗ 1000000 + x 2 32 ∗ 1000000 us =z*1000000 + \frac{x}{2^{32}}* 1000000 us=z∗1000000+232x∗1000000
此时,显然可以将分母理解为移位,然后将这个移位转到1000000上面,就变成了
u
s
=
z
∗
1000000
+
x
2
26
∗
15625
us =z*1000000 + \frac{x}{2^{26}}* 15625
us=z∗1000000+226x∗15625
来验证一下:
取小数部分移位26位(即取高6位): 6’b101100(44)
此时 us 为:
u
s
=
1
∗
1000000
+
44
∗
15625
=
687500
us =1*1000000 + 44 * 15625 = 687500
us=1∗1000000+44∗15625=687500
显然是不行的,这个截位带来的误差太大了,即便是取四舍五入,即取45,也是703125
此时, 我们可以转换一下思路,将属于小数的右移转换成整数部分的左移:
u
s
=
(
z
<
<
32
+
x
)
∗
1000000
2
32
us = \frac{(z<<32 + x)* 1000000}{2^{32}}
us=232(z<<32+x)∗1000000
这个时候来试一下:
u
s
=
(
1
<
<
32
+
3006477101
)
∗
1000000
2
32
=
1
,
699
,
999.99855645
us = \frac{(1<<32 + 3006477101)* 1000000}{2^{32}} = 1,699,999.99855645
us=232(1<<32+3006477101)∗1000000=1,699,999.99855645
当然,由于是整点乘除,结果只能是1699999.
当然了,也是因为这里是整点乘除,所以证明这个小算法还是可以继续修改下去的(对小数部分再作处理),就作为思考题就给大家啦~
还算可以吧~
结语
做点简单的小ip介绍应用系列给大家,但是最近应该还是暂时不会更之前的系列作,敬请等待:)