FPGA定点小数计算(Verilog版)第七篇——平方根倒数运算(使用John Carmack方法)

AI助手已提取文章相关产品:

更多精彩内容,请微信搜索“FPGAer俱乐部”关注我们。

有一段时间没有写博客了,突然想起前一段时间挖的坑,所以决定今天来填一下……其实,这一篇原本打算写的是采用牛顿迭代法的平方根运算的博文,现在改为平方根倒数运算,很显然就是之前的尝试失败了……

为什么说是失败了呢?主要原因就是相比于其他的求平方根方法:CORDIC和查表法,我的方法(先求平方根倒数,再求倒数)并没有足够的优势。可能有人会问,你是不是傻,求平方根就求平方根,非要搞这么复杂干什么?其实,这里的平方根倒数运算采用的是John Carmack的改进方法,基本上只进行一次牛顿迭代就可以满足大部分场合的精度要求,大家可以看一下我之前转载的一篇博文:

http://blog.chinaaet.com/justlxy/p/5100052325

事实证明,用Verilog语言设计的模块最终也达到了预期的理想效果(具体后面再说)。但是,问题出在除法上面,由于除法采用的是移位法(可以看一下我之前写的一篇整数的除法运算,小数的除法要比这个复杂一点:http://blog.chinaaet.com/justlxy/p/5100052068),如果采用迭代运算的方式,的确也用不了多少资源,但是运行速度(时钟速度)确难以提升上去,如果进行Pipelining的话,需要大量的触发器资源和LUT资源……想了好久也没有找到比较好的定点小数除法(或者求倒数)的实现方案,所以只能暂时放弃之前的想法了……如果有哪位有比较好的方法的,肯请赐教!感激不尽!

但是,忙活了这么多天了,还是有点成果的,所以就算把关于平方根倒数计算的设计整理一下,写一篇博文与大家分享一下。其实这个设计早就写好了,在前面几篇博文内容的基础上实现的平方根倒数计算的效果并不太好,花费了差不多1K的LUT和24个乘法器,最终只能运行到50MHz多一点的水平,实在不能忍了,所以又花了几天时间把所有的设计全部Pipelining了一遍,目前的实现效果如下:

具体可以参考该文档,文档中还详细描述了源码中的浮点小数和定点小数的数据格式:

fast_inverse_square_root.docx

使用Synplify Pro综合得到的RTL视图(顶层)如下:

从左到右,各模块依次为定点转换为浮点、浮点转换为定点,牛顿迭代……

所有逻辑均为Pipeline结构,其中定点到浮点的Latency为3个时钟,浮点到顶点为2个时钟,牛顿迭代为15个时钟。牛顿迭代中的乘法器采用了Pipeline的结构,每一级4个时钟的Latency。

牛顿迭代的详细RTL视图如下:

其中,乘法器单元是采用Lattice Clarity生成的Module。

本程序要实现的功能实际上就是下面的这一段C代码:

原理很简单,就不细说了。

目前,经过初步的优化和时序约束,设计可以稳定运行在200MHz(通过时序仿真,时序分析,和板上验证以及Reveal调试),如果需要,稍加优化,还可以将速度提升至250MHz左右……

由于代码太长了,再加上仿真脚本之类的,太多了,所以就不贴出来了,如果哪位有需要或者有兴趣的,可以联系我索取,E-mail:justlxy@mail.dhu.edu.cn

本文转载自电子技术应用ChinaAET公众号,如涉及侵权,请私信小编删除。

============华 丽 的 分 割 线============


想加入我们FPGA学习交流群吗?可以长按或扫描以下二维码,审核通过后我们邀请您加入

这些微信群旨在打造一个提供给FPGA工程开发人员及兴趣爱好者(统称“FPGAer”)进行技术交流、答疑解惑和学习的平台。而且我们也将会通过网络举办FPGA技术讲座,分享相关研究文献 



了解更多FPGA知识可以长按或扫描以下二维码关注FPGAer俱乐部




您可能感兴趣的与本文相关内容

### 三级标题:使用 Verilog 实现倒数表电路或模块 #### 设计思路与背景 在数字系统设计中,实现倒数表功能可以通过构建一个查找表(Look-Up Table, LUT)来完成。这种设计方式利用存储器单元保存预计算的结果,在运行时通过输入地址访问对应的倒数值[^1]。此外,也可以基于迭代算法动态计算倒数值,这种方法适用于无法预先定义所有可能输入的情况。 以下是两种主要设计方案: --- #### 方案一:基于 ROM 的静态查找表 (LUT) 此方案的核心思想是将所有的倒数值提前计算并存储在一个只读存储器(ROM)中。当接收到特定输入时,直接从 ROM 中取出对应的结果。这种方式的优点在于速度快、资源消耗低,但需要足够的存储空间来容纳整个表格数据。 ##### 代码实现 以下是一个简单的 Verilog 示例,展示如何创建一个固定的倒数表: ```verilog module reciprocal_table ( input [7:0] addr, // 输入地址(8位) output reg signed [9:0] data_out // 输出结果(带符号10位) ); // 定义倒数表大小为256条记录 reg signed [9:0] lut[0:255]; initial begin integer i; for(i = 0; i < 256; i = i + 1) begin if (i != 0) begin lut[i] = 256 / i; // 预先填充倒数表 end else begin lut[i] = 10'd0; // 处理分母为零的情况 end end end always @(addr) begin data_out <= lut[addr]; // 根据输入地址输出相应值 end endmodule ``` 上述代码实现了针对 8 位无符号整型输入的倒数表,其中 `lut` 数组用于存储每种可能输入下的倒数值[^3]。 --- #### 方案二:基于迭代公式的动态计算 如果目标设备缺乏足够的存储容量,则可以选择实时计算倒数值的方法。一种常见的技术是牛顿迭代法或其他逼近算法。该方法允许以较低的成本获得高精度的结果。 ##### 迭代公式推导 假设我们需要 \( y \approx \frac{1}{x} \),则可以应用如下更新规则: \[ X_{n+1} = X_n(2 - D'X_n) \] 此处 \( D' \) 表示标准化后的输入变量[^5]。 ##### 动态计算Verilog 实现 下面给出了一段简化Verilog 代码片段,演示了如何运用上述公式逐步接近真实倒数值: ```verilog module dynamic_reciprocal ( input wire clk, input wire reset, input wire start, input wire [7:0] din, // 输入数据 output reg ready_flag, // 计算完毕标志 output reg signed [9:0] dout // 结果输出 ); parameter ITERATIONS = 4; // 设置迭代次数 reg signed [9:0] xi_reg; integer iter_count; always @(posedge clk or posedge reset) begin if(reset) begin xi_reg <= 10'd1; // 初始化估计值 ready_flag <= 1'b0; iter_count <= 0; end else if(start && !ready_flag) begin case(iter_count) 0 : xi_reg <= {din[7], ~din[6:0]+1}; // 初值设置 default : xi_reg <= xi_reg * (2 - din * xi_reg); // 牛顿迭代 endcase if(iter_count >= ITERATIONS-1) begin ready_flag <= 1'b1; dout <= xi_reg; end else begin iter_count <= iter_count + 1; end end end endmodule ``` 这段程序展示了如何通过多次迭代逐渐趋近于精确解的过程[^4]。 --- #### 对比分析 | **特性** | **静态查找表(LUT)** | **动态计算** | |-----------------------|----------------------------------------|--------------------------------------| | 资源占用 | 较大(取决于表项数量) | 较小 | | 执行速度 | 极快 | 受限于迭代次数 | | 应用场景 | 已知范围内的固定输入 | 不确定范围内任意浮点/定点数支持 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值