当前位置: 首页 > article >正文

verilog实现开方运算/基于迭代法的平方根计算算法/FPGA实现开根号算法

        因老师要我们用verilog实现一个算法,涉及到开根号运算,正好学习一下算法,记录一下我的学习记录

主要算法:

    

要求:

        输入信号:input signed [15:0] a,      //数据a 

        输入信号:input signed [15:0] b,        //数据b

        输入信号:input [15:0] n,                   //数据个数

        输入信号:valid,                              //数据有效

        

        输出信号:output [15:0] result_int,        //输出结果整数部分

        输出信号:output [15:0] result_dec,        //输出结果小数部分

输入信号a,b为有符号数。

要求算法结果保留到小数点后三位

完整工程文件下载:verilog实现开根号算法完整工程 (点击蓝色字体获取)

1. 引言

  • 背景与动机: 平方根计算在硬件中的重要性,如在图像处理、信号处理、数字滤波器等领域的应用。
  • 设计目标: 实现一个高效的硬件平方根计算器,采用迭代法(如牛顿迭代法)来进行计算。
  • 文章结构: 概述文章结构,明确每一部分的内容,告诉读者你将从算法原理、硬件设计实现、性能分析等方面进行讨论。

2. 开发工具

开发平台:vivado

仿真平台:modelsim

3. 平方根计算的基本原理

3.1 平方根的定义

平方根是指一个数的平方等于给定数。例如,平方根计算问题可以表述为:给定一个非负数 SS,找到一个数 xx,使得 x2=Sx2=S。

3.2 牛顿迭代法

牛顿迭代法是一种常用的逼近函数根的方法,在求解平方根问题时具有广泛应用。牛顿迭代法用于平方根的计算公式如下:

Xn+1=1/2(Xn+S/Xn)

其中:

  • S 是我们要求平方根的数;
  • Xn​ 是第 n 次迭代的近似值;
  • Xn+1​ 是下一个迭代的近似值。

这个公式表示:通过当前的近似值 Xn 和 S,我们可以得到一个新的更接近的平方根值 Xn+1​。

3.3 迭代法的优点

牛顿迭代法具有以下优点:

  • 快速收敛: 牛顿法的收敛速度非常快,通常经过几次迭代即可获得很高的精度;
  • 实现简单: 只需要基本的加法、除法和位移操作,适合在硬件中实现。

4. verilog实现

总体设计框架

主要分为3大模块:

square_calculator:判断输入数据正负,并对其平方,组合逻辑

accumulator:对平方的数据进行求和,并除以n+1

sqrt:对上一个模块的结果,进行开根号运算,且在data_vaild为高的时候才算

4.1 square_calculator模块

        此模块主要是来判断输入数据正负,并对其进行平方,使用的是组合逻辑,所以并不需要时钟输入

代码设计:

module square_calculator (
    input signed[15:0] I,
    input signed[15:0] Q,
    output [31:0] I_squIre,
    output [31:0] Q_squIre
);

    reg signed [15:0] abs_I;
    reg signed [15:0] abs_Q;


    always @(*) begin
        if (I < 0)
            abs_I = -I;
        else
            abs_I = I;
    end

    always @(*) begin
        if (Q < 0)
            abs_Q = -Q;
        else
            abs_Q = Q;
    end
    
    assign I_squIre = abs_I * abs_I;
    assign Q_squIre = abs_Q * abs_Q;
endmodule

4.2 accumulator模块

        此模块主要用来计算根号内容,并输出对应的valid信号,但是为了满足精确到小数点后三位,需要把输入数据乘上1000_000,然后再开完根号之后再除以1000即可得到小数部分和整数部分。

部分代码设计:

module accumulator (
    input clk,
    input rst,
    input valid,
    input [15:0] n,
    input [31:0] in,
    output reg data_valid,
    output [63:0] sum
);
    reg [15:0] cnt;
    reg [63:0] sum_r;
    reg [63:0] sum_r2;
    always @(posedge clk or posedge rst) begin
        if (rst)
            begin
                sum_r <= 0;
                cnt <= 0;
            end
        else if (valid)
            begin
                cnt <= cnt + 1;
                sum_r <= sum_r + in * 32'd1000000;                
            end
        else if (cnt == n)
            begin
                sum_r <= 0;
                cnt <= 0;
            end
    end

    always @(posedge clk or posedge rst) begin
        if (rst)
            begin
                data_valid <= 0;
                sum_r2 <= 0;
            end
        else if (cnt == n)
            begin
                data_valid <= 1;
                sum_r2 <= sum_r;
            end 
        else 
            data_valid <= 0;
    end

    assign sum = sum_r2;

endmodule

4.3 sqrt模块

        此模块主要根据上个模块输出的数据和valid来进行开根号运算,并且分离出来整数部分和小数部分

部分代码设计:

//状态控制
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        sqrt_en <= 1'b0;
        icnt <= iteration_number - 1;
    end else if (!sqrt_en) begin  // 等待中
        if (din_valid_i) begin
            sqrt_en <= 1'b1;
            icnt <= iteration_number - 1;
            din_reg <= {{(DW % 2){1'b0}}, din_i};  // 输入扩展到偶数
            sqrt_data <= 0;
            rem_data <= 0;
        end
    end else begin  // 迭代中
        icnt <= icnt - 1;
        din_reg <= {din_reg[din_width-3:0], 2'b00};
        sqrt_data <= {sqrt_data[sqrt_width-2:0], sqrt_next};
        rem_data <= rem_next;
        if (icnt == 0) sqrt_en <= 1'b0;  // 结束迭代
    end
end

5. 仿真验证

编写仿真文件tb_sqrt_mean_calculator

`timescale 1ns / 1ps
module tb_sqrt_mean_calculator;
    reg clk, rst, valid;
    reg signed[15:0] I, Q, n;
    wire [15:0] result_int,result_dec;
    wire [31:0] result;

    sqrt_mean_calculator uut (
        .clk(clk),
        .rst(rst),
        .valid(valid),
        .I(I),
        .Q(Q),
        .n(n),
        .result(result),
        .result_int(result_int),
        .result_dec(result_dec)
    );

    initial begin
        clk = 0;
        rst = 1;
        valid = 0;
        #10 rst = 0;
        n = 16'd3;  // 数据总数为n+1

        // 输入数据
        #10 valid = 1; I = 16'd1; Q = 16'd2;  // 第1组数据
        #10 I = -16'd11; Q = 16'd12;  // 第2组数据
        #10 I = 16'd100; Q = -16'd112;  // 第3组数据
        #10 I = -16'd212; Q = -16'd252;  // 第4组数据
        #10 valid = 0;
        
        #600
        /*
        // 输入数据
        #10 vIlid = 1; I = 16'd4; Q = 16'd2;  // 第1组数据
        #10 I = 16'd7; Q = 16'd3;  // 第2组数据
        #10 I = 16'd3; Q = 16'd4;  // 第3组数据
        #10 I = 16'd2; Q = 16'd1;  // 第4组数据
        #10 I = 16'd5; Q = -16'd1;  // 第5组数据
        #10 vIlid = 0;
        #600
        */
        $stop;
    end

    always #5 clk = ~clk;
endmodule

5.1 理论值

首先先拿计算器运算一下,模拟数据

可以看出来,根据公式算出来为181.150,

5.2 实际值

接下来我们打开modelsim仿真看一下,得出的结果和计算值是否一致

由仿真结果可得,结果和计算值一样

制作不易,记得三连哦,给我动力,持续更新!!!


http://www.kler.cn/a/414416.html

相关文章:

  • AI 大模型如何重塑软件开发:未来的智能化变革
  • unity 绿幕抠图
  • Long类型实体对象返给前端精度丢失问题
  • 【算法 python A*算法的实现】
  • MySQL中的like模糊查询
  • wireshark基础
  • for (int i = 0, j = 0; ;){ 修改j }每次循环j会被重新赋值为0吗 详解
  • 【Python入门】Python数据类型
  • 【JavaEE初阶 — 网络编程】TCP流套接字编程
  • C语言——海龟作图(对之前所有内容复习)
  • 【单片机毕业设计12-基于stm32c8t6的智能称重系统设计】
  • Qt中QML和C++混合编程
  • 华为光学博士面试经验
  • 【AI系统】从 CUDA 对 AI 芯片思考
  • 未来已来?AI技术革新改变我们的生活
  • vscode自动打印日志插件
  • 【k8s深入理解之 Scheme 补充-1】理解 Scheme 中资源的注册以及 GVK 和 go 结构体的映射
  • 同时在github和gitee配置密钥
  • 力扣第 71 题 简化路径
  • 电脑模拟器端口号及相关的操作命令
  • 云计算基础-期末复习
  • 【Linux】文件管理
  • 华为Mate 70系列,行走在AI山脊
  • P1390 公约数的和
  • (73)脉冲幅度调制PAM调制解调通信系统的MATLAB仿真
  • 力扣hot100-->前缀和/前缀书/LRU缓存