verilog实现采样电流有效值的计算

上一篇文章中的代码也能实现有效值计算,仿真中可以实现,但在有些资源有限的芯片中,纯逻辑除法消耗资源过多,无法实现。

这篇文章主要通过两个除法IP核实现有效值计算

重点在于迭代过程中IP核的调用

除法运算中,start脉冲在未完成本次计算时只能在开始时刻有一个时钟的置1,其它时刻为0

c 复制代码
`timescale 1ns / 1ps
module current_rms_calculator_low (
    input wire clk_100MHZ,
    input wire rst_n,
    input [15:0] data_a,
    input sample_valid,
    output [15:0] rms_value_a,
    output reg rms_valid
);

parameter divisor = 200;

//=============================
// 平方
//=============================
reg [31:0] square;
always @(posedge clk_100MHZ) begin
    square <= data_a * data_a;
end

//=============================
// 200点累加
//=============================
reg [15:0] sample_cnt;
reg [39:0] sum;
wire calc_done = (sample_cnt == 199);
always @(posedge clk_100MHZ or negedge rst_n) begin
    if(!rst_n) begin
        sum        <= 0;
        sample_cnt <= 0;
    end
    else if(calc_done) begin
        sum        <= 0;
        sample_cnt <= 0;
    end
    else if(sample_valid) begin
        sum        <= sum + square;
        sample_cnt <= sample_cnt + 1'd1;
    end
end


//=============================
// 第一层除法IP:求平均值 sum / 200
//=============================
wire avg_divide_done;
wire [47:0] out_div_data;
wire [39:0] avg_value = out_div_data[47:8];

div_gen_0 inst_divide_40x8(
    .aclk(clk_100MHZ),
    .s_axis_divisor_tvalid(1'b1),
    .s_axis_divisor_tdata(divisor),
    .s_axis_dividend_tvalid(calc_done),
    .s_axis_dividend_tdata(sum),
    .m_axis_dout_tvalid(avg_divide_done),
    .m_axis_dout_tuser(),
    .m_axis_dout_tdata(out_div_data)
);

//=============================
// 第二层除法IP:牛顿迭代专用 sqrt_S / sqrt_x
//=============================
wire        sqrt_div_done;
wire [79:0] sqrt_div_result;
reg         sqrt_div_start;
wire [39:0] sqrt_quotient;

div_gen_40x40 inst_divide_sqrt(
    .aclk(clk_100MHZ),
    .s_axis_divisor_tvalid(sqrt_div_start),
    .s_axis_divisor_tdata(sqrt_x),       // 除数:sqrt_x
    .s_axis_dividend_tvalid(sqrt_div_start),
    .s_axis_dividend_tdata(sqrt_S),      // 被除数:sqrt_S
    .m_axis_dout_tvalid(sqrt_div_done),
    .m_axis_dout_tdata(sqrt_div_result)
);


assign sqrt_quotient = sqrt_div_result[79:40]; // 商


//=============================
// 牛顿迭代 三段式状态机
//=============================
reg [39:0] sqrt_x;
reg [39:0] sqrt_S;
reg [7:0]  newton_sqrt_cnt;

localparam IDLE            = 3'd0;
localparam state_DIV       = 3'd1;  // 这里调用除法IP
localparam state_SHIFT     = 3'd2;
localparam state_sqrt_DONE = 3'd3;

reg [2:0] curr_state;
reg [2:0] next_state;

// 第一段:状态寄存器
always @(posedge clk_100MHZ or negedge rst_n) begin
    if(!rst_n)
        curr_state <= IDLE;
    else
        curr_state <= next_state;
end

// 第二段:组合逻辑转移
always @(*) begin
    case(curr_state)
        IDLE: begin
            if(avg_divide_done)
                next_state = state_DIV;
            else
                next_state = IDLE;
        end

        state_DIV: begin
            if(sqrt_div_done)       // 等待IP除法完成
                next_state = state_SHIFT;
            else
                next_state = state_DIV;
        end

        state_SHIFT: begin
            if(newton_sqrt_cnt >= 16)
                next_state = state_sqrt_DONE;
            else
                next_state = state_DIV;
        end

        state_sqrt_DONE: begin
            next_state = IDLE;
        end

        default: next_state = IDLE;
    endcase
end

reg        div_start_en; 

// 第三段:时序输出逻辑
always @(posedge clk_100MHZ or negedge rst_n) begin
    if(!rst_n) begin
        sqrt_x         <= 0;
        sqrt_S         <= 0;
        newton_sqrt_cnt<= 0;
        div_start_en   <= 0;
        rms_valid      <= 0;
    end
    else begin
        case(curr_state)
            IDLE: begin
                newton_sqrt_cnt <= 0;
                rms_valid <= 0;
                div_start_en <= 0;           // 关闭启动
                if(avg_divide_done) begin
                    sqrt_S <= avg_value;     // 载入被开方数
                    sqrt_x <= avg_value >> 1;// 初始值
                end
            end

            // ==============================
            // ✅ 只启动一次除法IP,绝对安全
            // ==============================
            state_DIV: begin
                if(!div_start_en) begin
                    div_start_en   <= 1'b1;    // 只在第一次进入时启动
                    sqrt_div_start <= 1'b1;    // 发启动脉冲
                end
                else begin
                    sqrt_div_start <= 1'b0;    // 之后永远保持 0
                end

                // 除法完成,更新迭代值
                if(sqrt_div_done) begin
                    sqrt_x <= sqrt_x + sqrt_quotient;
                    div_start_en <= 1'b0;      // 复位启动标志
                end
            end

            state_SHIFT: begin
                sqrt_x <= sqrt_x >> 1;
                newton_sqrt_cnt <= newton_sqrt_cnt + 1'd1;
            end

            state_sqrt_DONE: begin
                rms_valid <= 1'b1;
            end
        endcase
    end
end

reg [15:0] reg_rms_value;

always @(posedge clk_100MHZ or negedge rst_n) begin
    if(!rst_n) begin
        reg_rms_value <= 16'd0;
    end
    else begin
        if(rms_valid == 1) begin
            reg_rms_value <= sqrt_x[15:0];
        end
        else begin
            reg_rms_value <= reg_rms_value;
        end
    end
end


assign rms_value_a = reg_rms_value;

endmodule
相关推荐
Aaron158818 小时前
RFSOC+VU13P+GPU 在6G互联网中的技术应用
大数据·人工智能·算法·fpga开发·硬件工程·信息与通信·信号处理
stars-he20 小时前
基于 Design Compiler 的 UDP Payload 追加控制模块综合与门级后仿真
笔记·fpga开发·udp
尤老师FPGA1 天前
HDMI数据的接收发送实验(十)
fpga开发
逻辑诗篇1 天前
破核拆解:PCIE719——基于Xilinx Zynq UltraScale+的高性能SAS扩展卡设计
fpga开发·架构
逻辑诗篇2 天前
高性能存储扩展利器|PCIE719 基于Zynq UltraScale+的企业级可编程SAS方案
fpga开发
liuluyang5302 天前
SV主要关键词详解
fpga开发·uvm·sv
happyDogg_2 天前
验证环境采样rtl时序数据遇到的问题
fpga开发
unicrom_深圳市由你创科技2 天前
项目分析和FPGA器件选型外包服务包括哪些内容?别让选错芯片毁了整个项目
fpga开发
Aaron15882 天前
27DR/47DR/67DR技术对比及应用分析
人工智能·算法·fpga开发·硬件架构·硬件工程·信息与通信·基带工程
my_daling2 天前
DSMC通信协议理解,以及如何在FPGA上实现DSMC从设备(2)
学习·fpga开发