数字信号处理的基石——从模拟世界到数字世界的桥梁
数字信号处理(Digital Signal Processing,DSP)是利用数字计算方法对信号进行变换、滤波、分析等技术处理的一门学科。与模拟信号处理相比,DSP具有以下核心优势:
| 特性 | 模拟处理 | 数字处理(DSP) |
|---|---|---|
| 精度 | 受元件容差限制 | 由字长决定,可精确控制 |
| 稳定性 | 受温度/老化影响 | 软件实现,不受环境影响 |
| 灵活性 | 修改需改硬件 | 修改算法即可 |
| 可重复性 | 存在偏差 | 完全可重复 |
| 复杂算法 | 难以实现 | 可实现任意复杂算法 |
| 功耗 | 通常较低 | 较高(高速运算) |
一个完整的DSP系统包含以下关键组件,从天线接收的模拟信号到最终数字处理结果的完整链路:
模数转换器(ADC)是连接模拟世界与数字世界的桥梁。其核心参数包括:
DSP处理器为信号处理算法优化了以下硬件特性:
在DSP中,我们按不同维度对信号进行分类:
| 类型 | 时间 | 幅度 | 示例 |
|---|---|---|---|
| 模拟信号 | 连续 | 连续 | 麦克风输出 |
| 离散信号 | 离散 | 连续 | 采样保持输出 |
| 数字信号 | 离散 | 量化(离散) | ADC输出 |
我们从最基础的DSP硬件模块开始——一个可配置的数字信号发生器,能产生正弦波、方波、三角波和锯齿波四种基本波形。
//=============================================
// dsp_signal_generator.v
// 多波形数字信号发生器
// 功能:产生正弦波/方波/三角波/锯齿波
//=============================================
module dsp_signal_generator #(
parameter DATA_WIDTH = 16, // 输出数据位宽
parameter PHASE_WIDTH = 12, // 相位累加器位宽
parameter LUT_DEPTH = 256 // 正弦查找表深度
)(
input wire clk, // 系统时钟
input wire rst_n, // 异步复位,低有效
input wire [1:0] wave_sel, // 波形选择:00=正弦 01=方波 10=三角 11=锯齿
input wire [PHASE_WIDTH-1:0] freq_word, // 频率控制字(决定输出频率)
output reg [DATA_WIDTH-1:0] data_out, // 波形输出
output wire [PHASE_WIDTH-1:0] phase_out // 当前相位输出
);
// 相位累加器
reg [PHASE_WIDTH-1:0] phase_acc;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
phase_acc <= {PHASE_WIDTH{1'b0}};
else
phase_acc <= phase_acc + freq_word;
end
assign phase_out = phase_acc;
// 正弦查找表(1/4周期压缩存储)
// 完整256点正弦查找表
reg [DATA_WIDTH-1:0] sine_lut [0:LUT_DEPTH-1];
initial begin
// 初始化正弦查找表
// 正弦值范围:0 ~ 2^(DATA_WIDTH-1)-1(无符号表示)
integer i;
for (i = 0; i < LUT_DEPTH; i = i + 1) begin
sine_lut[i] = $rtoi(
(2.0**(DATA_WIDTH-1) - 1.0) *
$sin(2.0 * 3.14159265358979 * i / LUT_DEPTH)
+ 2.0**(DATA_WIDTH-1)
);
end
end
// 查找表地址(取相位高8位)
wire [7:0] lut_addr = phase_acc[PHASE_WIDTH-1:PHASE_WIDTH-8];
// 正弦波输出
wire [DATA_WIDTH-1:0] sine_out = sine_lut[lut_addr];
// 方波输出:相位在0~π时为高,π~2π时为低
wire [DATA_WIDTH-1:0] square_out = phase_acc[PHASE_WIDTH-1] ?
{1'b0, {(DATA_WIDTH-1){1'b0}}} : // 低电平
{1'b0, {(DATA_WIDTH-1){1'b1}}}; // 高电平
// 三角波输出
// 前半周期递增,后半周期递减
wire [DATA_WIDTH-1:0] triangle_out;
wire [PHASE_WIDTH-2:0] phase_lower = phase_acc[PHASE_WIDTH-2:0];
wire phase_msb = phase_acc[PHASE_WIDTH-1];
assign triangle_out = phase_msb ?
({DATA_WIDTH{1'b1}} - ({1'b0, phase_lower, {(DATA_WIDTH-PHASE_WIDTH+1){1'b0}}})) :
({1'b0, phase_lower, {(DATA_WIDTH-PHASE_WIDTH+1){1'b0}}});
// 锯齿波输出:直接使用相位累加器值
wire [DATA_WIDTH-1:0] sawtooth_out = {1'b0, phase_acc[PHASE_WIDTH-2:0],
{(DATA_WIDTH-PHASE_WIDTH+1){1'b0}}};
// 波形选择多路器
always @(*) begin
case (wave_sel)
2'b00: data_out = sine_out;
2'b01: data_out = square_out;
2'b10: data_out = triangle_out;
2'b11: data_out = sawtooth_out;
endcase
end
endmodule
//=============================================
// tb_signal_generator.v
// 多波形信号发生器测试台
//=============================================
`timescale 1ns/1ps
module tb_signal_generator;
parameter DATA_WIDTH = 16;
parameter PHASE_WIDTH = 12;
parameter LUT_DEPTH = 256;
parameter CLK_PERIOD = 10; // 100MHz时钟
reg clk;
reg rst_n;
reg [1:0] wave_sel;
reg [PHASE_WIDTH-1:0] freq_word;
wire [DATA_WIDTH-1:0] data_out;
wire [PHASE_WIDTH-1:0] phase_out;
dsp_signal_generator #(
.DATA_WIDTH(DATA_WIDTH),
.PHASE_WIDTH(PHASE_WIDTH),
.LUT_DEPTH(LUT_DEPTH)
) uut (
.clk(clk),
.rst_n(rst_n),
.wave_sel(wave_sel),
.freq_word(freq_word),
.data_out(data_out),
.phase_out(phase_out)
);
// 时钟生成
initial clk = 0;
always #(CLK_PERIOD/2) clk = ~clk;
// 测试流程
initial begin
$dumpfile("signal_gen.vcd");
$dumpvars(0, tb_signal_generator);
// 初始化
rst_n = 0;
wave_sel = 2'b00;
freq_word = 12'h010; // 低频
#100;
rst_n = 1;
// 测试正弦波
wave_sel = 2'b00;
freq_word = 12'h010;
#50000;
$display("正弦波测试完成,最终输出 = %d", data_out);
// 测试方波
wave_sel = 2'b01;
freq_word = 12'h020;
#50000;
$display("方波测试完成,最终输出 = %d", data_out);
// 测试三角波
wave_sel = 2'b10;
freq_word = 12'h015;
#50000;
$display("三角波测试完成,最终输出 = %d", data_out);
// 测试锯齿波
wave_sel = 2'b11;
freq_word = 12'h008;
#50000;
$display("锯齿波测试完成,最终输出 = %d", data_out);
// 频率扫描测试
wave_sel = 2'b00;
freq_word = 12'h001;
#20000;
freq_word = 12'h040;
#20000;
freq_word = 12'h100;
#20000;
$display("频率扫描测试完成");
$display("=== 所有测试通过 ===");
$finish;
end
endmodule
DSP硬件中,浮点运算代价高昂,定点数是主流选择。下面实现核心的定点数运算模块:
//=============================================
// dsp_fixed_point.v
// 定点数运算库
// 格式:Q1.(DATA_WIDTH-1) 有符号定点数
//=============================================
// 定点数乘法器
module dsp_fixed_mul #(
parameter A_WIDTH = 16,
parameter B_WIDTH = 16,
parameter OUT_WIDTH = 16
)(
input wire signed [A_WIDTH-1:0] a,
input wire signed [B_WIDTH-1:0] b,
output wire signed [OUT_WIDTH-1:0] result
);
// 乘法结果位宽 = A_WIDTH + B_WIDTH
wire signed [A_WIDTH+B_WIDTH-1:0] product = a * b;
// Q格式调整:两个Q1.(N-1)相乘得Q2.(2N-2),需右移(N-1)位回到Q1.(N-1)
// 同时截断到输出位宽
assign result = product >>> (A_WIDTH - 1);
endmodule
// 定点数加法器(带饱和)
module dsp_fixed_add_sat #(
parameter WIDTH = 16
)(
input wire signed [WIDTH-1:0] a,
input wire signed [WIDTH-1:0] b,
output reg signed [WIDTH-1:0] result
);
wire signed [WIDTH:0] sum = {a[WIDTH-1], a} + {b[WIDTH-1], b};
always @(*) begin
if (sum > {(WIDTH+1){1'b0}} + {{WIDTH{1'b0}}, 1'b0} +
(2**(WIDTH-1) - 1))
result = {1'b0, {(WIDTH-1){1'b1}}}; // 正饱和
else if (sum < -2**(WIDTH-1))
result = {1'b1, {(WIDTH-1){1'b0}}}; // 负饱和
else
result = sum[WIDTH-1:0];
end
endmodule
// 绝对值运算
module dsp_abs #(
parameter WIDTH = 16
)(
input wire signed [WIDTH-1:0] data_in,
output wire [WIDTH-1:0] data_out
);
assign data_out = data_in[WIDTH-1] ? (~data_in + 1'b1) : data_in;
endmodule
以下模块演示采样保持与零阶保持重建过程:
//=============================================
// dsp_sample_hold.v
// 采样保持与重建模块
//=============================================
module dsp_sample_hold #(
parameter DATA_WIDTH = 16,
parameter DECIM_RATE = 4 // 降采样率
)(
input wire clk,
input wire rst_n,
input wire signed [DATA_WIDTH-1:0] analog_in, // 高速率输入
output reg signed [DATA_WIDTH-1:0] sampled_out, // 采样输出
output reg signed [DATA_WIDTH-1:0] zoh_out, // 零阶保持重建输出
output reg sample_valid // 采样有效脉冲
);
reg [$clog2(DECIM_RATE)-1:0] decim_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
decim_cnt <= 0;
sampled_out <= 0;
zoh_out <= 0;
sample_valid <= 1'b0;
end else begin
decim_cnt <= decim_cnt + 1'b1;
sample_valid <= 1'b0;
if (decim_cnt == DECIM_RATE - 1) begin
// 采样时刻
sampled_out <= analog_in;
zoh_out <= analog_in; // 零阶保持
sample_valid <= 1'b1;
decim_cnt <= 0;
end
// zoh_out保持不变,实现零阶保持效果
end
end
endmodule
| 指标 | 定义 | 典型值 |
|---|---|---|
| 采样率 | 每秒采样点数 | 44.1kHz~192kHz(音频) |
| 动态范围 | 最大/最小可表示信号比 | 96dB(16bit)~120dB(20bit) |
| SFDR | 无杂散动态范围 | >80dB(优质ADC) |
| 处理延迟 | 输入到输出的时间差 | 与滤波器阶数相关 |
| MIPS | 每秒百万条指令 | 取决于时钟和架构 |
| MAC/s | 每秒乘加次数 | DSP核心性能指标 |
若系统时钟为100MHz,相位累加器位宽为32位,频率控制字为0x01000000,输出信号频率为多少?
提示:f_out = f_clk × freq_word / 2^N
一个12位ADC,参考电压为3.3V,计算量化步长Δ和理论SNR。
在 dsp_signal_generator.v 中添加频率调制(FM)功能:使用第二个相位累加器作为调制信号,叠加到频率控制字上。
对于Q1.15格式的定点数,计算以下运算的精度损失:
✅ 理解了模拟/数字信号处理的区别
✅ 掌握了DSP系统基本架构(ADC→处理→DAC)
✅ 实现了多波形数字信号发生器(Verilog)
✅ 掌握了定点数运算原理
✅ 理解了采样保持与零阶保持重建
DSP概述在进阶应用中还有以下重要主题值得深入研究:
本课内容在整个DSP课程体系中处于承上启下的位置:
在实际工程中,DSP概述的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。
在实际工程中,DSP概述的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。
在实际工程中,DSP概述的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。