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
相关推荐
fei_sun2 小时前
牛客Verilog刷题篇
fpga开发
my_daling6 小时前
DSMC通信协议理解,以及如何在FPGA上实现DSMC从设备(1)
学习·fpga开发
fei_sun17 小时前
FPGA&数字前端
fpga开发
尤老师FPGA17 小时前
HDMI数据的接收发送实验(九)
fpga开发
Flamingˢ19 小时前
ZYNQ + OV5640 视频系统开发(四):HDMI 显示链路
嵌入式硬件·fpga开发·硬件架构·音视频
LCMICRO-1331084774620 小时前
国产长芯微LDC5141完全P2P替代DAC80501,数模转换器 (DAC)
单片机·嵌入式硬件·fpga开发·硬件工程·dsp开发·数模转换器 dac
Nobody331 天前
锁存器与触发器
fpga开发
Nobody331 天前
跨时钟域信号处理的办法有哪些
fpga开发·信号处理
LCMICRO-133108477461 天前
长芯微LPC556D1完全P2P替代DAC8830,是引脚兼容的16位数模转换器,该系列产品为单通道、低功耗、缓冲电压输出型DAC
stm32·单片机·嵌入式硬件·fpga开发·硬件工程·电压输出型dac