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
相关推荐
鸥梨菌Honevid43 分钟前
STA 与 CDC
fpga开发
科恒盛远1 小时前
ZU902 与 FMC200 高速采集示波器实测
fpga开发
NamoAmitabha1282 小时前
日常问答1---deepseek
fpga开发
鸥梨菌Honevid3 小时前
异步FIFO为什么用打两拍的格雷码?
fpga开发
努力搬砖的小H4 小时前
FPGA应用技巧之AXI4-FULL-MASTER IP FDMA(Fast Direct Memory)详细介绍
经验分享·fpga开发·fdma·axi总线·米联客
szxinmai主板定制专家21 小时前
电力边缘 AI 落地难点剖析:RK 系列 + FPGA 异构平台在储能监控终端的 EMC 与算力优化
人工智能·fpga开发·arm+fpga·rk3588+fpga
szxinmai主板定制专家1 天前
基于RK3588+FPGA+AI的储能电站边缘网关,实现电力数据采集与智能预警
arm开发·人工智能·fpga开发·rk3576·半导体设备
XMAIPC_Robot1 天前
储能控制器产品化:ARM+FPGA整机高低温、EMC可靠性测试实战总结
arm开发·人工智能·fpga开发·arm+fpga·rk3588+fpga
szxinmai主板定制专家1 天前
面向虚拟电厂协调控制:RK3588+FPGA+AI边缘控制器设计思路与算力评估
人工智能·嵌入式硬件·fpga开发·机器人·jetson·zynq·振动采集