上一篇文章中的代码也能实现有效值计算,仿真中可以实现,但在有些资源有限的芯片中,纯逻辑除法消耗资源过多,无法实现。
这篇文章主要通过两个除法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