突破传统分频思维用Verilog实现动态可调的数控分频器在FPGA开发中分频器是最基础也最常用的模块之一。大多数初学者接触的第一个分频器实现方案就是简单的计数器——通过累加时钟边沿来实现固定比例的分频。这种方案虽然简单直接但缺乏灵活性和可扩展性。今天我们将一起探索一种更高级的分频器实现方式数控分频器。数控分频器的核心优势在于其可编程特性。与固定分频比的传统分频器不同数控分频器允许我们在运行时动态调整分频比例只需改变输入的控制数据即可。这种特性使得它在需要频率灵活变化的场景中特别有用比如软件定义无线电(SDR)、动态时钟管理等应用。1. 数控分频器的核心原理1.1 从固定分频到数控分频的演进传统分频器通常采用计数器实现其分频比在代码中固定。例如一个简单的4分频器Verilog实现可能如下module fixed_divider( input clk, output reg clk_out ); reg [1:0] counter 0; always (posedge clk) begin counter counter 1; if (counter 1) clk_out ~clk_out; end endmodule这种实现虽然简单但分频比被硬编码在逻辑中无法在运行时改变。而数控分频器通过引入数据输入端口将分频比作为可配置参数module programmable_divider( input clk, input [3:0] div_ratio, // 分频比控制字 output reg clk_out ); reg [3:0] counter 0; always (posedge clk) begin if (counter div_ratio - 1) begin counter 0; clk_out ~clk_out; end else begin counter counter 1; end end endmodule1.2 数控分频的数学基础数控分频器的核心数学原理可以表示为f_out f_in / (2 × N)其中f_in输入时钟频率N控制字分频系数f_out输出时钟频率值得注意的是这种实现方式实际上产生的是占空比为50%的方波因此分频系数需要乘以2。如果需要非对称波形或其他特殊波形算法需要相应调整。1.3 分频器性能指标对比特性固定分频器数控分频器分频比灵活性低高资源占用少中等最大工作频率高中等实现复杂度简单中等动态调整能力无有2. Quartus II中的完整实现流程2.1 工程创建与基本设置在Quartus II中创建新工程的步骤如下启动Quartus II选择File → New Project Wizard指定工程目录和名称避免使用中文路径选择目标FPGA器件型号根据实际开发板选择跳过EDA工具设置除非有特殊需求完成工程创建提示建议为每个新工程创建独立的目录避免文件混乱。工程名称最好能反映项目内容如programmable_divider。2.2 Verilog代码实现细节我们来实现一个完整的数控分频器模块支持动态分频比调整module dynamic_divider #( parameter WIDTH 8 // 控制字位宽 )( input wire clk, // 输入时钟 input wire rst_n, // 异步复位(低有效) input wire [WIDTH-1:0] div_ratio, // 分频比控制 output reg clk_out // 分频后时钟 ); reg [WIDTH-1:0] counter; always (posedge clk or negedge rst_n) begin if (!rst_n) begin counter 0; clk_out 0; end else begin if (counter div_ratio - 1) begin counter 0; clk_out ~clk_out; end else begin counter counter 1; end end end endmodule这段代码增加了几个重要改进参数化设计通过WIDTH参数可灵活调整控制字位宽复位功能增加了异步复位信号提高系统可靠性完整的时序控制确保在分频比变化时也能正确工作2.3 测试平台与仿真验证为了验证我们的设计需要编写测试平台(Testbench)timescale 1ns/1ps module tb_dynamic_divider; reg clk 0; reg rst_n 0; reg [7:0] div_ratio 8d4; wire clk_out; // 生成50MHz时钟(周期20ns) always #10 clk ~clk; // 实例化被测模块 dynamic_divider #(.WIDTH(8)) uut ( .clk(clk), .rst_n(rst_n), .div_ratio(div_ratio), .clk_out(clk_out) ); initial begin // 初始复位 #20 rst_n 1; // 测试不同分频比 #1000 div_ratio 8d2; // 4分频 #1000 div_ratio 8d4; // 8分频 #1000 div_ratio 8d8; // 16分频 #1000 $stop; end endmodule在ModelSim中运行仿真可以观察到输出时钟频率随div_ratio变化而改变验证了设计的正确性。3. 硬件交互与动态控制3.1 拨码开关接口设计为了让用户能够动态调整分频比我们可以使用开发板上的拨码开关作为输入。假设使用4位拨码开关Verilog代码需要相应调整module top_divider( input wire clk_50M, // 50MHz主时钟 input wire [3:0] sw, // 4位拨码开关输入 output wire led // 分频输出指示 ); // 内部信号声明 wire clk_2M; wire rst_n 1b1; // 假设复位常高 // 预分频50MHz→2MHz clock_divider #( .DIV_RATIO(25) ) pre_div ( .clk_in(clk_50M), .rst_n(rst_n), .clk_out(clk_2M) ); // 数控分频器 dynamic_divider #( .WIDTH(4) ) main_div ( .clk(clk_2M), .rst_n(rst_n), .div_ratio(sw 1), // 避免0分频 .clk_out(led) ); endmodule3.2 管脚分配策略在Quartus II中完成编译后需要进行管脚分配。常见的分配方式如下打开Assignments → Pin Planner根据开发板原理图为每个端口指定具体管脚保存并重新编译典型的管脚分配表示例信号名称管脚编号对应硬件clk_50MPIN_G1晶振输出sw[0]PIN_AB12拨码开关0sw[1]PIN_AC12拨码开关1sw[2]PIN_AF9拨码开关2sw[3]PIN_AF10拨码开关3ledPIN_V16LED指示灯3.3 实际效果观察完成烧录后可以通过以下步骤验证功能拨动开关设置不同的二进制值如0001、0010、0100等观察LED的闪烁频率变化记录不同设置下的实际频率与理论值比较注意人眼对高于约25Hz的闪烁无法分辨因此建议选择较大的分频比使LED闪烁频率在1-10Hz范围内便于观察。4. 高级优化与扩展应用4.1 奇数分频实现技巧前面的实现主要针对偶数分频输出占空比50%。奇数分频需要更复杂的逻辑module odd_divider #( parameter N 5 // 奇数分频比 )( input clk, input rst_n, output clk_out ); reg [2:0] cnt; reg clk_p, clk_n; // 生成两个相位差180度的时钟 always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 0; clk_p 0; end else begin if (cnt N-1) cnt 0; else cnt cnt 1; if (cnt (N-1)/2) clk_p 1; else clk_p 0; end end always (negedge clk or negedge rst_n) begin if (!rst_n) clk_n 0; else begin if (cnt (N-1)/2) clk_n 1; else clk_n 0; end end // 合并两个时钟得到50%占空比 assign clk_out clk_p | clk_n; endmodule4.2 小数分频技术对于需要非整数分频比的场景可以采用小数分频技术。常见的方法包括脉冲删除法周期性删除部分时钟脉冲双模分频法交替使用两个整数分频比DDS技术使用直接数字频率合成以下是双模分频的Verilog示例module fractional_divider #( parameter M 3, // 分子 parameter N 2 // 分母 )( input clk, output reg clk_out ); reg [31:0] acc 0; always (posedge clk) begin acc acc M; if (acc N) begin acc acc - N; clk_out ~clk_out; end end endmodule4.3 性能优化建议流水线设计对于高分频比可将计数器分段时钟门控在不需要输出变化时关闭时钟多相时钟生成同时产生多个相位差时钟跨时钟域同步当分频比动态变化时需要同步控制信号下表对比了不同优化技术的效果优化技术资源消耗最大频率功耗实现复杂度基本实现低高中低流水线设计中很高中中时钟门控低高低中多相时钟高中高高5. 实际应用案例分析5.1 音频采样率转换在数字音频处理中经常需要在不同采样率之间转换。数控分频器可以用来生成不同的采样时钟module audio_clock_gen( input wire clk_48M, // 主时钟48MHz input wire [15:0] ratio,// 采样率控制 output wire sclk // 采样时钟 ); // 48MHz / (256×ratio) 目标采样率 // 例如ratio187 → 48M/(256×187) ≈ 1002Hz dynamic_divider #(.WIDTH(16)) div_stage1 ( .clk(clk_48M), .rst_n(1b1), .div_ratio(8d255), .clk_out() ); dynamic_divider #(.WIDTH(16)) div_stage2 ( .clk(clk_48M), .rst_n(1b1), .div_ratio(ratio), .clk_out(sclk) ); endmodule5.2 通信系统中的时钟恢复在异步通信系统中数控分频器可用于时钟恢复电路动态调整本地时钟频率以匹配输入数据速率module clock_recovery( input wire clk_ref, // 参考时钟 input wire data_in, // 输入数据 output wire clk_rec // 恢复时钟 ); // 相位检测逻辑(简化版) wire phase_early, phase_late; phase_detector pd ( .clk_ref(clk_ref), .data(data_in), .early(phase_early), .late(phase_late) ); // 分频比控制逻辑 reg [7:0] div_ratio 8d10; always (posedge clk_ref) begin case ({phase_early, phase_late}) 2b10: div_ratio div_ratio - 1; // 频率太高 2b01: div_ratio div_ratio 1; // 频率太低 default: ; // 保持 endcase end // 数控分频器 dynamic_divider #(.WIDTH(8)) divider ( .clk(clk_ref), .rst_n(1b1), .div_ratio(div_ratio), .clk_out(clk_rec) ); endmodule5.3 电机控制PWM生成在电机控制应用中数控分频器可用于生成可调频率的PWM信号module pwm_generator( input wire clk, input wire [7:0] freq_ctrl, // 频率控制 input wire [7:0] duty_ctrl, // 占空比控制 output wire pwm_out ); reg [7:0] counter 0; reg pwm 0; always (posedge clk) begin if (counter freq_ctrl - 1) begin counter 0; pwm 1; end else begin counter counter 1; if (counter duty_ctrl) pwm 0; end end assign pwm_out pwm; endmodule
别再只会用计数器了!用Verilog在Quartus II里玩转数控分频器,从原理到烧录一步到位
发布时间:2026/5/31 5:22:40
突破传统分频思维用Verilog实现动态可调的数控分频器在FPGA开发中分频器是最基础也最常用的模块之一。大多数初学者接触的第一个分频器实现方案就是简单的计数器——通过累加时钟边沿来实现固定比例的分频。这种方案虽然简单直接但缺乏灵活性和可扩展性。今天我们将一起探索一种更高级的分频器实现方式数控分频器。数控分频器的核心优势在于其可编程特性。与固定分频比的传统分频器不同数控分频器允许我们在运行时动态调整分频比例只需改变输入的控制数据即可。这种特性使得它在需要频率灵活变化的场景中特别有用比如软件定义无线电(SDR)、动态时钟管理等应用。1. 数控分频器的核心原理1.1 从固定分频到数控分频的演进传统分频器通常采用计数器实现其分频比在代码中固定。例如一个简单的4分频器Verilog实现可能如下module fixed_divider( input clk, output reg clk_out ); reg [1:0] counter 0; always (posedge clk) begin counter counter 1; if (counter 1) clk_out ~clk_out; end endmodule这种实现虽然简单但分频比被硬编码在逻辑中无法在运行时改变。而数控分频器通过引入数据输入端口将分频比作为可配置参数module programmable_divider( input clk, input [3:0] div_ratio, // 分频比控制字 output reg clk_out ); reg [3:0] counter 0; always (posedge clk) begin if (counter div_ratio - 1) begin counter 0; clk_out ~clk_out; end else begin counter counter 1; end end endmodule1.2 数控分频的数学基础数控分频器的核心数学原理可以表示为f_out f_in / (2 × N)其中f_in输入时钟频率N控制字分频系数f_out输出时钟频率值得注意的是这种实现方式实际上产生的是占空比为50%的方波因此分频系数需要乘以2。如果需要非对称波形或其他特殊波形算法需要相应调整。1.3 分频器性能指标对比特性固定分频器数控分频器分频比灵活性低高资源占用少中等最大工作频率高中等实现复杂度简单中等动态调整能力无有2. Quartus II中的完整实现流程2.1 工程创建与基本设置在Quartus II中创建新工程的步骤如下启动Quartus II选择File → New Project Wizard指定工程目录和名称避免使用中文路径选择目标FPGA器件型号根据实际开发板选择跳过EDA工具设置除非有特殊需求完成工程创建提示建议为每个新工程创建独立的目录避免文件混乱。工程名称最好能反映项目内容如programmable_divider。2.2 Verilog代码实现细节我们来实现一个完整的数控分频器模块支持动态分频比调整module dynamic_divider #( parameter WIDTH 8 // 控制字位宽 )( input wire clk, // 输入时钟 input wire rst_n, // 异步复位(低有效) input wire [WIDTH-1:0] div_ratio, // 分频比控制 output reg clk_out // 分频后时钟 ); reg [WIDTH-1:0] counter; always (posedge clk or negedge rst_n) begin if (!rst_n) begin counter 0; clk_out 0; end else begin if (counter div_ratio - 1) begin counter 0; clk_out ~clk_out; end else begin counter counter 1; end end end endmodule这段代码增加了几个重要改进参数化设计通过WIDTH参数可灵活调整控制字位宽复位功能增加了异步复位信号提高系统可靠性完整的时序控制确保在分频比变化时也能正确工作2.3 测试平台与仿真验证为了验证我们的设计需要编写测试平台(Testbench)timescale 1ns/1ps module tb_dynamic_divider; reg clk 0; reg rst_n 0; reg [7:0] div_ratio 8d4; wire clk_out; // 生成50MHz时钟(周期20ns) always #10 clk ~clk; // 实例化被测模块 dynamic_divider #(.WIDTH(8)) uut ( .clk(clk), .rst_n(rst_n), .div_ratio(div_ratio), .clk_out(clk_out) ); initial begin // 初始复位 #20 rst_n 1; // 测试不同分频比 #1000 div_ratio 8d2; // 4分频 #1000 div_ratio 8d4; // 8分频 #1000 div_ratio 8d8; // 16分频 #1000 $stop; end endmodule在ModelSim中运行仿真可以观察到输出时钟频率随div_ratio变化而改变验证了设计的正确性。3. 硬件交互与动态控制3.1 拨码开关接口设计为了让用户能够动态调整分频比我们可以使用开发板上的拨码开关作为输入。假设使用4位拨码开关Verilog代码需要相应调整module top_divider( input wire clk_50M, // 50MHz主时钟 input wire [3:0] sw, // 4位拨码开关输入 output wire led // 分频输出指示 ); // 内部信号声明 wire clk_2M; wire rst_n 1b1; // 假设复位常高 // 预分频50MHz→2MHz clock_divider #( .DIV_RATIO(25) ) pre_div ( .clk_in(clk_50M), .rst_n(rst_n), .clk_out(clk_2M) ); // 数控分频器 dynamic_divider #( .WIDTH(4) ) main_div ( .clk(clk_2M), .rst_n(rst_n), .div_ratio(sw 1), // 避免0分频 .clk_out(led) ); endmodule3.2 管脚分配策略在Quartus II中完成编译后需要进行管脚分配。常见的分配方式如下打开Assignments → Pin Planner根据开发板原理图为每个端口指定具体管脚保存并重新编译典型的管脚分配表示例信号名称管脚编号对应硬件clk_50MPIN_G1晶振输出sw[0]PIN_AB12拨码开关0sw[1]PIN_AC12拨码开关1sw[2]PIN_AF9拨码开关2sw[3]PIN_AF10拨码开关3ledPIN_V16LED指示灯3.3 实际效果观察完成烧录后可以通过以下步骤验证功能拨动开关设置不同的二进制值如0001、0010、0100等观察LED的闪烁频率变化记录不同设置下的实际频率与理论值比较注意人眼对高于约25Hz的闪烁无法分辨因此建议选择较大的分频比使LED闪烁频率在1-10Hz范围内便于观察。4. 高级优化与扩展应用4.1 奇数分频实现技巧前面的实现主要针对偶数分频输出占空比50%。奇数分频需要更复杂的逻辑module odd_divider #( parameter N 5 // 奇数分频比 )( input clk, input rst_n, output clk_out ); reg [2:0] cnt; reg clk_p, clk_n; // 生成两个相位差180度的时钟 always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 0; clk_p 0; end else begin if (cnt N-1) cnt 0; else cnt cnt 1; if (cnt (N-1)/2) clk_p 1; else clk_p 0; end end always (negedge clk or negedge rst_n) begin if (!rst_n) clk_n 0; else begin if (cnt (N-1)/2) clk_n 1; else clk_n 0; end end // 合并两个时钟得到50%占空比 assign clk_out clk_p | clk_n; endmodule4.2 小数分频技术对于需要非整数分频比的场景可以采用小数分频技术。常见的方法包括脉冲删除法周期性删除部分时钟脉冲双模分频法交替使用两个整数分频比DDS技术使用直接数字频率合成以下是双模分频的Verilog示例module fractional_divider #( parameter M 3, // 分子 parameter N 2 // 分母 )( input clk, output reg clk_out ); reg [31:0] acc 0; always (posedge clk) begin acc acc M; if (acc N) begin acc acc - N; clk_out ~clk_out; end end endmodule4.3 性能优化建议流水线设计对于高分频比可将计数器分段时钟门控在不需要输出变化时关闭时钟多相时钟生成同时产生多个相位差时钟跨时钟域同步当分频比动态变化时需要同步控制信号下表对比了不同优化技术的效果优化技术资源消耗最大频率功耗实现复杂度基本实现低高中低流水线设计中很高中中时钟门控低高低中多相时钟高中高高5. 实际应用案例分析5.1 音频采样率转换在数字音频处理中经常需要在不同采样率之间转换。数控分频器可以用来生成不同的采样时钟module audio_clock_gen( input wire clk_48M, // 主时钟48MHz input wire [15:0] ratio,// 采样率控制 output wire sclk // 采样时钟 ); // 48MHz / (256×ratio) 目标采样率 // 例如ratio187 → 48M/(256×187) ≈ 1002Hz dynamic_divider #(.WIDTH(16)) div_stage1 ( .clk(clk_48M), .rst_n(1b1), .div_ratio(8d255), .clk_out() ); dynamic_divider #(.WIDTH(16)) div_stage2 ( .clk(clk_48M), .rst_n(1b1), .div_ratio(ratio), .clk_out(sclk) ); endmodule5.2 通信系统中的时钟恢复在异步通信系统中数控分频器可用于时钟恢复电路动态调整本地时钟频率以匹配输入数据速率module clock_recovery( input wire clk_ref, // 参考时钟 input wire data_in, // 输入数据 output wire clk_rec // 恢复时钟 ); // 相位检测逻辑(简化版) wire phase_early, phase_late; phase_detector pd ( .clk_ref(clk_ref), .data(data_in), .early(phase_early), .late(phase_late) ); // 分频比控制逻辑 reg [7:0] div_ratio 8d10; always (posedge clk_ref) begin case ({phase_early, phase_late}) 2b10: div_ratio div_ratio - 1; // 频率太高 2b01: div_ratio div_ratio 1; // 频率太低 default: ; // 保持 endcase end // 数控分频器 dynamic_divider #(.WIDTH(8)) divider ( .clk(clk_ref), .rst_n(1b1), .div_ratio(div_ratio), .clk_out(clk_rec) ); endmodule5.3 电机控制PWM生成在电机控制应用中数控分频器可用于生成可调频率的PWM信号module pwm_generator( input wire clk, input wire [7:0] freq_ctrl, // 频率控制 input wire [7:0] duty_ctrl, // 占空比控制 output wire pwm_out ); reg [7:0] counter 0; reg pwm 0; always (posedge clk) begin if (counter freq_ctrl - 1) begin counter 0; pwm 1; end else begin counter counter 1; if (counter duty_ctrl) pwm 0; end end assign pwm_out pwm; endmodule