数据计算的深度学习 FPGA 实现 - 4.定点算术步骤
然而(话锋一转),在大多数场合下,不需要以上这么多位来保留计算结果,因为我们在进行数学运算时,已经知道输入数据的大致范围,一个数除以1000和除以1结果数据所需最小位宽能一样么?加减运算的操作步骤是先对齐小数点位数,后加减。而乘法是先计算后取小数点。这实际上与十进制运算一致,我们看看具体的计算步骤:
整数之间加减以及乘法的统一步骤:预估结果位宽N --> 按照结果位宽扩展操作数符号位以防止溢出 --> 运算取低N位。
定点小数加减运算步骤:预估结果位宽N --> 得到结果小数点后位数 --> 对齐操作数整数位和小数位,确定扩展位宽M(M≥N) --> 加减运算取低M位。
定点小数乘法运算步骤:预估结果位宽N --> 得到结果小数点后位数 --> 扩展操作数位宽 --> 相乘取低N位
5. 变量与常量运算化简
以上讨论的均是两变量之间的运算规则,当然结果位宽及格式准则是适用的。变量与常量的运算的优势在于,可以将乘除法转换成加减以及移位运算实现,从而降低计算复杂度和延迟。当常数项C为2的整数次幂(C = 2^p),则乘C等于变量左移p位,除以C等于变量右移p位。几个在书中看到的几个简单示例:A*16 = A <<4A*20 = A<<4 + A<<2.A除以2 = A >>1A除以3 = A*(0.25+0.0625+0.0156) = A>>2+A>>4+A>>6A除以5 = A*(0.125+0.0625+0.0156) = A>>3 + A>>4 + A>>6.其中乘法完全等价对应的移位相加操作,而除法的移位代替会损失精度。
三、如何计算特殊函数
FPGA内部的DSP Slice可以直接进行最基本的加法和乘法运算,但是对于其他比如对数、指数、三角函数、开根号等特殊函数就无能为力了。这时需要借助算法对这些特殊函数进行变换和简化。FPGA实现复杂函数的常用手段一个是级数展开,再一个就是CORDIC算法。关于CORDIC的理论知识和具体内容详见参考文献2,这里主要阐述CORDIC的IP核调用以及应用示例。CORDIC算法就是通过一定的手段,将很多复杂的特殊函数变为相加移位运算,这一点对于硬件芯片实现来说非常友好。CORDIC分为旋转模式和矢量模式,配合圆周坐标、线性坐标和双曲线坐标会有六种组合,具体见下表:
从表中发现,基本的乘除法、三角函数、反三角函数、双曲函数、反双曲函数、开根号都能够直接求得,那其他函数怎么办?
常见的函数计算需求基本都能满足,虽上述变换式对自变量定义域有限制,但同样可以分析输入数据的取值范围并利用简单的数学变换得到想要的结果。Xilinx同时提供了浮点IP核以及CORDIC IP核,前者调用简单但占用资源大,延迟高,因此利用CORDIC算法计算函数是个较好的选择。
四、CORDIC计算e^x Demo
1. 算法仿真分析
要计算e^x数值需要让CORDIC工作在双曲坐标的旋转模式下,通过e^x = sinhx+coshx关系式间接求得。首先看下sinh和cosh函数的曲线,有个直观认识。
我们用MATLAB毫不费力地验证一下公式正确性:
在设计后也同样要借助MATLAB进行仿真验证。
2. CORDIC IP核
现在通过查看user guide得知CORDIC IP核的接口及主要特性。
接口包括输入笛卡尔数据输入通道、相位输入通道、全局信号以及数据输出通道。该IP核有两种结构:串行和并行,可根据数据吞吐量需求选择,并行结构可以每个时钟输出一个计算结果。如果计算sinh和cosh,要向phase通道输入相位信息,X_OUT是cosh(phase),Y_OUT是sinh(phase).输入phase必须满足数据范围,否则出现不可预计结果。输出帧结构及数据范围如下:
其中输入数据格式为2QN,输出则是1QN。由于均是有符号数,也就是输入整数部分3bit,输出整数部分2bit。接下来对IP核进行配置,重点是第一页,此处将其配置为计算sinh和cosh模式,采用并行优化的流水线结构。相位以角度为单位,输入输出位宽设置成16bit。
3.HDL代码设计及仿真验证
设计代码:
1 `timescale 1ns / 1ps 2 3 module cordic_ex#(parameter DIN_W = 16, 4 DOUT_W = 16) 5 ( 6 input clk, 7 input [DIN_W-1:0] din,//2Q13 8 input din_vld, 9 10 output reg [DOUT_W+1-1:0] dout = 0,//2Q14 11 output reg dout_vld = 0 12 ); 13 14 15 wire [DOUT_W*2-1 : 0] m_axis_dout_tdata; 16 wire m_axis_dout_tvalid; 17 wire signed [DOUT_W-1:0] sinh,cosh; 18 19 // ex = sinhx + coshx <1Q14+1Q14 = 2Q14> 20 always @(posedge clk)begin 21 dout <= sinh + cosh; 22 end 23 24 assign sinh = m_axis_dout_tdata[DOUT_W*2-1 -:DOUT_W]; 25 assign cosh = m_axis_dout_tdata[DOUT_W-1 -:DOUT_W]; 26 27 always @(posedge clk)begin 28 if(m_axis_dout_tvalid)begin 29 dout_vld <= 1'b1; 30 end 31 else 32 dout_vld <= 0; 33 end 34 35 cordic_0 cordic_cosh_sinh ( 36 .aclk(clk), // input wire aclk 37 .s_axis_phase_tvalid(din_vld), // input wire s_axis_phase_tvalid 38 .s_axis_phase_tdata(din), // input wire [15 : 0] s_axis_phase_tdata 39 .m_axis_dout_tvalid(m_axis_dout_tvalid), // output wire m_axis_dout_tvalid 40 .m_axis_dout_tdata(m_axis_dout_tdata) // output wire [31 : 0] m_axis_dout_tdata 41 ); 42 43 endmodule
用MATLAB产生两组数据,并将角度值定点化后作为设计模块数据激励:
testbench:
1 `timescale 1ns / 1ps 2 3 module cordic_ex_tb(); 4 5 parameter CYC = 20; 6 7 reg clk; 8 reg [16-1:0] din; 9 reg din_vld; 10 11 wire signed [17-1:0] dout; 12 wire dout_vld; 13 14 cordic_ex#(.DIN_W(16), 15 .DOUT_W(16)) 16 uut( 17 .clk (clk) , 18 .din (din) ,//2Q13 19 .din_vld (din_vld) , 20 .dout (dout) ,//2Q14 21 .dout_vld (dout_vld) 22 ); 23 24 initial begin 25 clk = 1; 26 forever #(CYC/2) clk = ~clk; 27 end 28 29 initial begin 30 #1; 31 din = 0; 32 din_vld = 0; 33 #(CYC*10); 34 35 din_vld = 1; 36 din = 16'b0001010000011011;//pi * 1/5 37 #(CYC*1); 38 din = 16'b1110011011011110;//-pi * 1/4 39 #5; 40 $stop; 41 end 42 43 endmodule
仿真结果:
仿真波形表明,计算结果与MATLAB浮点运算相近,满足一般计算需求。若想提高精度,可以增加CORDIC输出数据位宽。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
参考文献:
1 ug479 7 Series DSP48E1 Slice User Guide.
2 Xilinx CORDIC算法(非常经典)_图文_百度文库 https://wenku.baidu.com/view/6c623aa8910ef12d2bf9e732.html
上一篇: 结构光的相移法+多频离群器数学原理推导
下一篇: 什么是 qpsk 调制
推荐阅读
-
[姿势估计] 实践记录:使用 Dlib 和 mediapipe 进行人脸姿势估计 - 本文重点介绍方法 2):方法 1:基于深度学习的方法:。 基于深度学习的方法:基于深度学习的方法利用深度学习模型,如卷积神经网络(CNN)或递归神经网络(RNN),直接从人脸图像中学习姿势估计。这些方法能够学习更复杂的特征表征,并在大规模数据集上取得优异的性能。方法二:基于二维校准信息估计三维姿态信息(计算机视觉 PnP 问题)。 特征点定位:人脸姿态估计的第一步是通过特征点定位来检测和定位人脸的关键点,如眼睛、鼻子和嘴巴。这些关键点提供了人脸的局部结构信息,可用于后续的姿势估计。 旋转表示:常见的旋转表示方法包括欧拉角和旋转矩阵。欧拉角通过三个旋转角度(通常是俯仰、偏航和滚动)描述头部的旋转姿态。旋转矩阵是一个 3x3 矩阵,表示头部从一个坐标系到另一个坐标系的变换。 三维模型重建:根据特征点的定位结果,三维人脸模型可用于姿势估计。通过将人脸的二维图像映射到三维模型上,可以估算出人脸的旋转和平移信息。这就需要建立人脸的三维模型,然后通过优化方法将模型与特征点对齐,从而获得姿势估计结果。 特征点定位 特征点定位是用于检测人脸关键部位的五官基础部分,还有其他更多的特征点表示方法,大家可以参考我上一篇文章中介绍的特征点检测方案实践:人脸校正二次定位操作来解决人脸校正的问题,客户在检测关键点的代码上略有修改,坐标转换部分客户见上图 def get_face_info(image). img_copy = image.copy image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_detection.process(image) # 在图像上绘制人脸检测注释。 image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) box_info, facial = None, None if results.detections: for detection in results. for detection in results.detections: mp_drawing.Drawing.detection = 无 mp_drawing.draw_detection(image, detection) 面部 = detection.location_data.relative_keypoints 返回面部 在上述代码中,返回的数据是五官(6 个关键点的坐标),这是用 mediapipe 库实现的,下面我们可以尝试用另一个库:dlib 来实现。 使用 dlib 使用 Dlib 库在 Python 中实现人脸关键点检测的步骤如下: 确保已安装 Dlib 库,可使用以下命令: pip install dlib 导入必要的库: 加载 Dlib 的人脸检测器和关键点检测器模型: 读取图像并将其灰度化: 使用人脸检测器检测图像中的人脸: 对检测到的人脸进行遍历,并使用关键点检测器检测人脸关键点: 显示绘制了关键点的图像: 以下代码将参数 landmarks_part 添加到要返回的关键点坐标中。
-
数据计算的深度学习 FPGA 实现 - 4.定点算术步骤