2. 前台校准的偏移校正
任务定义
本案例针对高速ADC中由器件失配、输入直流偏置等因素引入的固定偏移误差,实现一种前台校准的偏移校正方案。要求在每次上电或用户触发时,将ADC输入端短接到模拟地,采集N次样本并计算均值作为偏移估计值,随后在正常转换结果中减去该估计值,完成数字域的偏移校正。校正后的输出应使零输入条件下的平均码值接近理想中点(如12位ADC的理想零输入码值2048)。
实现方案
采用Verilog实现一个带偏移校准功能的ADC数字后端模块。模块包含两个工作阶段:校准时,输入信号被强制置零(模拟前端短接),状态机控制累加器采集M=16个样本并求平均,得到偏移量;正常工作时,输入数据减去该偏移量后输出。校准使能信号cal_en上升沿触发一次校准流程,完成后自动进入正常工作模式。
Verilog实现
module adc_offset_cal #(
parameter DATA_WIDTH = 12,
parameter ACCUM_NUM = 4 // 累加2^4=16个样本
)(
input wire clk,
input wire rst_n,
input wire cal_en, // 校准触发(上升沿有效)
input wire [DATA_WIDTH-1:0] adc_in, // ADC原始输出
output reg [DATA_WIDTH-1:0] data_out, // 校正后输出
output reg cal_done // 校准完成标志
);
// 状态定义
localparam IDLE = 2'd0;
localparam CAL_ACCUM = 2'd1;
localparam CAL_DONE = 2'd2;
localparam NORMAL = 2'd3;
reg [1:0] state, next_state;
reg [ACCUM_NUM-1:0] sample_cnt;
reg [DATA_WIDTH+ACCUM_NUM-1:0] accum_sum;
reg [DATA_WIDTH-1:0] offset_reg;
reg cal_en_d1, cal_en_rise;
// 上升沿检测
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cal_en_d1 <= 1'b0;
end else begin
cal_en_d1 <= cal_en;
end
end
assign cal_en_rise = cal_en & ~cal_en_d1;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
// 状态转移
always @(*) begin
next_state = state;
case (state)
IDLE: if (cal_en_rise) next_state = CAL_ACCUM;
CAL_ACCUM: if (sample_cnt == {ACCUM_NUM{1'b1}}) next_state = CAL_DONE;
CAL_DONE: next_state = NORMAL;
NORMAL: if (cal_en_rise) next_state = CAL_ACCUM;
default: next_state = IDLE;
endcase
end
// 校准累加与偏移计算
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sample_cnt <= 0;
accum_sum <= 0;
offset_reg <= {DATA_WIDTH{1'b0}};
cal_done <= 1'b0;
end else begin
case (state)
CAL_ACCUM: begin
accum_sum <= accum_sum + adc_in;
sample_cnt <= sample_cnt + 1'b1;
cal_done <= 1'b0;
end
CAL_DONE: begin
// 右移ACCUM_NUM位求平均
offset_reg <= accum_sum[ACCUM_NUM +: DATA_WIDTH];
cal_done <= 1'b1;
end
NORMAL: begin
cal_done <= 1'b0;
end
default: begin
accum_sum <= 0;
sample_cnt <= 0;
end
endcase
end
end
// 正常模式输出校正
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
data_out <= {DATA_WIDTH{1'b0}};
else if (state == NORMAL)
data_out <= adc_in - offset_reg;
else
data_out <= adc_in; // 校准期间直通
end
endmodule
测试平台与验证
module tb_adc_offset_cal;
reg clk = 0;
reg rst_n = 0;
reg cal_en = 0;
reg [11:0] adc_in = 0;
wire [11:0] data_out;
wire cal_done;
adc_offset_cal #(.DATA_WIDTH(12), .ACCUM_NUM(4)) dut(
.clk(clk), .rst_n(rst_n), .cal_en(cal_en),
.adc_in(adc_in), .data_out(data_out), .cal_done(cal_done)
);
always #5 clk = ~clk;
initial begin
// 复位
#10 rst_n = 1;
// 模拟输入偏移:零输入时ADC输出为2060(理想2048,偏移+12)
adc_in = 2060;
// 触发校准
#10 cal_en = 1;
#10 cal_en = 0;
// 等待校准完成(16个时钟周期累加 + 1周期计算)
wait(cal_done);
#10;
// 校准后正常输出应接近2048
$display("Offset estimate: %0d", dut.offset_reg);
$display("Calibrated output: %0d", data_out);
// 验证:偏移估计应为12左右,输出应为2048左右
if (dut.offset_reg == 12 && data_out == 2048)
$display("TEST PASSED");
else
$display("TEST FAILED");
#50 $finish;
end
endmodule
运行与判定标准
运行方式:使用支持Verilog-2001的仿真器(如Vivado XSim、ModelSim、Icarus Verilog)编译并运行上述两个文件。在Vivado中可创建工程添加两个文件后运行行为仿真。
验证方法:观察仿真打印信息。测试平台模拟了零输入时ADC输出为2060(即存在+12 LSB偏移)的场景。校准完成后,offset_reg应等于12(16个2060累加后右移4位得到2060,减去理想中点2048得12),data_out应为2048。
判定标准:仿真输出打印Offset estimate: 12、Calibrated output: 2048且显示TEST PASSED,即证明前台偏移校准功能正确。若偏移估计值或校正输出与预期不符,需检查累加位宽是否足够、状态机时序是否正确。