资讯动态

数字IC设计实战:脉冲密度调制(PDM)核心原理与Verilog实现

发布时间:2026/8/7 15:43:06 来源:尧图企业网站定制
1. 项目概述从一道笔试真题看PDM的实战价值最近在帮朋友复盘一些数字IC设计的笔试面试题发现“脉冲密度调制”这个考点出现的频率相当高尤其是几家头部公司的真题里它常常作为手撕代码环节的压轴题。乍一看“脉冲密度调制”这个词有点学术但说白了它就是一种用数字脉冲的“疏密”来表征模拟信号幅度大小的技术。你在音频DAC、Class-D放大器、电机控制里都能见到它的身影。对于数字IC工程师而言这道题考察的绝不仅仅是写个计数器或者状态机那么简单它是对你系统思维、时序设计、面积与功耗权衡能力的综合检验。今天我就以一道典型的公司笔试真题为例把PDM从头到尾的设计思路、代码实现、仿真验证以及那些容易栽跟头的“坑”彻底讲透。无论你是正在备战秋招春招的同学还是想巩固基础的在职工程师相信这篇结合实战的解析都能让你有所收获。2. 真题需求深度拆解与方案选型2.1 题目还原与核心需求解析通常这类笔试题会给出类似如下的描述设计一个脉冲密度调制器。输入一个N位宽的数字输入信号din[N-1:0]代表一个无符号整数。 输出一个1比特的脉冲密度调制信号pdm_out。 功能在每一个时钟周期pdm_out输出高电平的概率应与din的数值成正比。具体地若din的满量程值为MAX_VAL 2^N - 1则pdm_out 1的概率应为din / MAX_VAL。 要求设计尽可能高效考虑面积和时序。我们需要把这段文字描述转化为可执行的技术规格概率映射核心是将一个静态的N位数字转化为一个动态的、统计特性符合要求的单比特流。例如din128假设N8MAX_VAL255则长期统计下来pdm_out输出1的占比应接近128/255 ≈ 50.2%。实时性每个时钟周期都要产生一个输出比特输出只依赖于当前时钟周期的计算不依赖于过去很长时间的历史这与PWM不同PWM依赖于固定周期内的占空比。无记忆性要求题目通常不要求输出比特流在短周期内如一个MAX_VAL周期内严格精确匹配比例而是追求长期统计下的准确性。这给了我们设计上的灵活性。高效性“高效”在数字IC中通常指向逻辑资源少面积小、关键路径短时序好、功耗低。2.2 核心方案对比累加器与比较器实现PDM最经典、最直接的方法就是基于累加器的结构也称为“一阶Δ-Σ调制器”。这是笔试和实践中绝对的主流方案我们必须掌握。它的核心思想是用一个累加器不断地将输入值din累加起来同时用一个阈值通常是累加器的位宽去比较根据比较结果产生输出并将输出反馈回来影响累加过程。具体来说我们定义一个位宽为(N1)位的累加寄存器acc。为什么是N1位因为累加器可能会溢出我们需要额外的1位来容纳溢出位确保计算正确。每个时钟周期将输入din加到累加器acc上acc acc din。将累加器acc的最高位MSB作为输出pdm_out。因为acc是(N1)位其MSB可以看作是一个值为2^N的权重。同时为了完成反馈我们需要“扣除”已输出的部分。一个巧妙且高效的做法是将输出的pdm_out左移N位后从累加器中减去。即acc acc din - (pdm_out N)。为什么这样操作这需要理解其物理意义。acc可以看作是一个“误差积分器”。当acc累积的值超过2^N时MSB为1我们输出1这代表我们“分配”了一个满量程单位2^N。同时我们必须从积分器中减去这个已分配的量2^N否则误差会一直累积导致后续统计完全错误。pdm_out N正是这个2^N的等效表示当pdm_out1时其值为2^N为0时值为0。这种实现方式只需要一个加法器、一个减法器和一些寄存器非常高效。另一种易于理解但效率稍低的实现是“比较与扣减”法定义一个(N1)位的寄存器sum。每个周期sum sum din。如果sum 2^N则pdm_out1并且sum sum - 2^N否则pdm_out0。这种方法逻辑清晰但需要比较器在硬件上可能比直接取MSB的方式占用更多资源。方案选定对于笔试和大多数应用“取MSB并反馈减”的方案是首选。因为它将比较操作简化为直接取一个固定位的值节省了一个数值比较器关键路径更短。接下来我们的设计将基于此方案展开。3. 模块设计与代码实现详解3.1 接口定义与参数化设计一个健壮的IP设计首先要考虑通用性。我们将模块设计为参数化以便适应不同的输入位宽N。module pdm_modulator #( parameter N 8 // 输入数据位宽默认8位 )( input wire clk, // 时钟 input wire rst_n, // 低电平有效异步复位 input wire [N-1:0] din, // N位无符号输入数据 output reg pdm_out // 1位PDM输出 );参数化意义N决定了输入数据的精度和满量程值MAX_VAL 2**N - 1。N8时对应255级N10时对应1023级。参数化设计使得模块可以像乐高积木一样被复用这是工程师专业性的体现。3.2 核心累加器与状态更新逻辑这是整个设计的核心。我们需要一个位宽为N1的累加寄存器acc。// 内部寄存器定义 reg [N:0] acc; // 累加器位宽N1最高位(bit[N])用于输出 // 时序逻辑每个时钟周期更新 always (posedge clk or negedge rst_n) begin if (!rst_n) begin // 异步复位清零累加器和输出 acc {(N1){1b0}}; pdm_out 1b0; end else begin // PDM核心算法 // 1. 计算新的累加值旧累加值 输入 - 反馈 // 反馈 (上一周期的输出 pdm_out) 左移 N 位 // 注意这里的pdm_out是上一拍寄存器的值 acc acc din - ({pdm_out, {N{1b0}}}); // 关键步骤 // 2. 根据当前更新前的累加器最高位决定输出 // 注意此处的acc是上一时钟周期的值这是标准的寄存器输出 pdm_out acc[N]; // 取累加器的最高位作为输出 end end代码逐行解析与注意事项reg [N:0] acc;[N:0]表示位宽为N1。例如N8则acc为[8:0]共9位。acc[8]是最高位。acc acc din - ({pdm_out, {N{1b0}}});这是最核心的一行。acc din完成输入值的累加。{pdm_out, {N{1b0}}}这是一个位拼接操作。pdm_out是1比特{N{1b0}}是N比特的0。将它们拼接起来就得到了一个N1位的数。当pdm_out为1时这个数的值就是1 N即2^N当pdm_out为0时这个数就是0。这完美实现了“减去2^N”或“不减”的逻辑。关键时序这里减去的pdm_out是always块开始时寄存器的值也就是上一个时钟周期的输出结果。这确保了反馈的正确性。pdm_out acc[N];输出由累加器的最高位直接决定。注意这里赋值的acc同样是上一周期的值。这意味着当前周期的输出是由上一周期累加器计算后的状态决定的。整个系统的延迟是1个时钟周期。一个极易混淆的坑有些初学者会想写成acc acc din; pdm_out (acc din)[N];。这犯了两个错误一是没有反馈误差会爆炸二是使用了阻塞赋值与非阻塞赋值的混合在组合逻辑中会产生不可预测的后果且不符合时序电路设计规范。我们的写法是标准的时序电路描述每个时钟沿用上一周期的状态acc,pdm_out计算新的状态新的acc, 新的pdm_out。3.3 行为仿真与结果分析设计完RTL代码必须用仿真来验证其功能。我们编写一个简单的测试平台。module tb_pdm(); parameter N 8; reg clk, rst_n; reg [N-1:0] din; wire pdm_out; // 实例化设计 pdm_modulator #(.N(N)) uut ( .clk(clk), .rst_n(rst_n), .din(din), .pdm_out(pdm_out) ); // 生成时钟 initial begin clk 0; forever #5 clk ~clk; // 100MHz时钟 end // 施加激励 initial begin // 初始化 rst_n 0; din 0; #20 rst_n 1; // 释放复位 // 测试用例1输入为中间值 128/255 ≈ 50% din 8d128; #1000; // 运行足够多的周期进行统计 // 测试用例2输入为小值 32/255 ≈ 12.5% din 8d32; #1000; // 测试用例3输入为最大值 255/255 100% din 8d255; #1000; // 测试用例4输入为最小值 0/255 0% din 8d0; #1000; $finish; end // 实时监控与简单统计在仿真窗口中观察 integer cycle_count, high_count; real duty_cycle; always (posedge clk) begin if (rst_n) begin cycle_count cycle_count 1; if (pdm_out) high_count high_count 1; // 每256个周期打印一次统计信息可选 if (cycle_count !0 (cycle_count % 256 0)) begin duty_cycle 100.0 * high_count / cycle_count; $display(Time%0t, din%d, Cycles%d, High%d, Duty Cycle%.2f%%, $time, din, cycle_count, high_count, duty_cycle); end end else begin cycle_count 0; high_count 0; end end endmodule仿真结果分析 运行仿真后我们观察波形和打印信息。以din128为例在几千个时钟周期后统计到的高电平占比会非常接近50%。pdm_out的波形看起来是一串随机的0和1但在统计意义上其“密度”精确对应了输入值。对于din255理论上应该始终输出1。但在我们的实现中由于每个周期都会减去2^Nacc会在一个稳定状态附近波动几乎每个周期acc[N]都是1但偶尔可能会出现0。这是Δ-Σ调制器固有的“极限环”振荡现象对于满量程输入一个更严格的设计可能会强制输出恒为1。但笔试中通常理解这个原理即可。4. 深入原理噪声整形与性能考量4.1 为什么是“一阶Δ-Σ”我们之前的设计在信号处理领域被称为一阶Δ-Σ调制器。理解这个背景能让你在面试中脱颖而出。ΔDelta代表差分。在我们的算法中acc累加的是输入din与反馈量pdm_out N的差值。这个差值可以看作是一种“量化误差”。ΣSigma代表求和积分。acc寄存器正是在对上述的差值进行累加积分。调制器它将高精度的数字输入N位转换为低精度但高速的单比特流。其神奇之处在于噪声整形。量化误差即实际输出与理想值之间的细微差别被反馈回路和积分器推向了高频区域。在后续通过一个简单的模拟低通滤波器例如一个RC电路后高频噪声被滤除还原出的模拟信号精度可以远高于单比特本身所代表的精度。这就是为什么1比特的PDM流可以还原出高质量音频的原因。4.2 关键参数与设计折衷过采样率我们的系统时钟频率f_clk与输入数据更新频率f_data的比值。在笔试题目中通常隐含f_data f_clk即每个时钟周期输入都可能变化。但在实际应用中如音频f_clk如3.072 MHz远高于f_data如48 kHz这就是过采样。过采样率越高噪声被推到的频率越高越容易滤除信噪比越好。累加器位宽我们选择了N1位。这是最低要求。如果位宽更大例如NM位可以提供更大的“容错”空间减少极限环振荡但也会增加硬件面积。对于一阶调制器N1位是经典且足够的选择。输出频谱与谐波失真由于反馈的非线性PDM输出频谱中除了基波和量化噪声还可能存在谐波成分。高阶Δ-Σ调制器如二阶、三阶能更好地压制低频噪声和谐波但电路更复杂稳定性分析也更困难。笔试通常只考一阶。5. 常见问题、调试技巧与设计扩展5.1 笔试与面试中常见追问Q如果输入din是负数有符号数怎么办A核心算法不变但需要处理有符号数的加减。将din定义为signed类型并将累加器acc也扩展为有符号数。反馈减法部分逻辑不变。关键在于acc的最高位符号位不能直接作为输出了因为累加值可能为负。此时输出逻辑通常变为pdm_out (acc 0) ? 1‘b1 : 1’b0;。这实际上是将双极性信号编码到了单比特流中输出1代表正0代表负或零。Q如何验证输出概率的正确性A通过长时间统计。在仿真中像我们测试平台那样计数总周期数T和高电平数H计算H/T。这个比值应无限接近din / (2^N - 1)。统计的周期数越多结果越精确。这是验证功能最直接的方法。Q这个设计的最大时钟频率受什么限制A受关键路径延迟限制。关键路径是acc从寄存器输出经过一个加法器加din再经过一个减法器减反馈最后写回acc寄存器。这条路径的组合逻辑延迟决定了f_max。为了提速可以采用流水线技术但会引入额外的处理延迟。Q面积主要消耗在哪里A主要是一个(N1)位的加法器和一个(N1)位的减法器实际上通常合并为一个加减器。以及一个(N1)位的寄存器。对于N8或16面积非常小。5.2 设计扩展高阶Δ-Σ调制器如果笔试要求设计二阶PDM你需要知道思路。二阶调制器使用两个积分器两个累加器传递函数更复杂噪声整形能力更强将更多量化噪声推向高频。 一个简化的二阶结构伪代码如下// 两个累加器 reg [N1:0] acc1; // 一阶积分 reg [N1:0] acc2; // 二阶积分 always (posedge clk) begin // 一阶积分 acc1 acc1 din - feedback; // 二阶积分 acc2 acc2 acc1 - feedback; // 注意这里用acc1作为输入 // 输出和反馈生成有多种量化器结构常见的是将acc2与阈值比较 pdm_out (acc2 0) ? 1b1 : 1b0; feedback (pdm_out) ? (1 (N1)) : 0; // 反馈量可能需要调整 end二阶设计需要仔细处理系数量化和稳定性面试时能说出基本结构和优势更好的信噪比即可。5.3 从RTL到GDSII的思考虽然笔试只到RTL但作为工程师我们要有更远的视野综合我们的代码是典型的同步时序逻辑综合工具能很好地将其映射为加法器、寄存器和选择器。时钟门控如果din长时间不变可以考虑对累加器加法链进行时钟门控以降低动态功耗。DFT需要确保累加器寄存器acc是可扫描的以方便芯片测试。验证完备性除了行为仿真还应编写定向测试用例边界值如0, MAX_VAL、随机测试并计算输出信号的统计特性如信噪比、总谐波失真与数学模型进行对比。回过头看这道“手撕代码”题它完美地串联了数电基础计数器、状态机、信号处理知识过采样、噪声整形和工程实践面积时序权衡、代码风格。下次再遇到它希望你能从容地画出结构图写出简洁优雅的Verilog代码并清晰地阐述其背后的原理与折衷。这才是公司真正想考察的潜力。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价