当前位置: 首页 > news >正文

分频器实战:从秒脉冲到任意分频的Verilog实现与仿真

1. 分频器基础概念与秒脉冲生成

在数字电路设计中,分频器是最基础也最常用的模块之一。简单来说,分频器的作用就是将输入时钟信号的频率降低到我们需要的频率。比如把24MHz的时钟变成1Hz的秒脉冲,这就是典型的"秒分频"应用。

我刚开始接触FPGA时,第一个实验就是实现秒脉冲生成。当时用的是经典的50MHz开发板,需要从50MHz分频到1Hz。这个过程中踩过不少坑,比如计数器位数不够导致分频错误,或者复位信号处理不当导致计数器无法正常工作。

下面是一个典型的秒脉冲生成模块的Verilog实现(以24MHz时钟为例):

module second_pulse( input clk, // 24MHz系统时钟 input rst_n, // 低电平复位 output reg pulse // 秒脉冲输出 ); reg [24:0] counter; // 24MHz时钟需要25位计数器 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 0; pulse <= 0; end else begin if(counter == 24_000_000 - 1) begin // 计数到24M-1 counter <= 0; pulse <= 1; // 产生1个时钟周期的高电平 end else begin counter <= counter + 1; pulse <= 0; end end end endmodule

这个模块的工作原理很简单:每当计数器计满24,000,000个时钟周期(正好1秒),就输出一个时钟周期的高电平脉冲。我在实际项目中发现,这种设计有个小问题:如果时钟频率不是整数MHz,计算起来会比较麻烦。后来我改进成了参数化的设计,可以根据不同的输入频率自动计算计数值。

2. 偶数分频的实现技巧

偶数分频是最简单的分频方式,比如2分频、4分频、6分频等。它的特点是实现简单,而且可以保证输出时钟的占空比为50%。这对于很多同步电路设计来说非常重要。

我常用的偶数分频实现方法是这样的:对于N分频(N为偶数),我们只需要在计数器计到N/2-1时翻转输出时钟信号即可。比如要实现6分频:

module even_divider #( parameter DIV = 6 // 分频系数,必须为偶数 )( input clk, input rst_n, output reg clk_out ); reg [3:0] counter; // 计数器 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 0; clk_out <= 0; end else if(counter == (DIV/2 - 1)) begin counter <= 0; clk_out <= ~clk_out; // 翻转时钟 end else begin counter <= counter + 1; end end endmodule

在实际工程中,我遇到过一个问题:当分频系数很大时(比如1000分频),直接用这种方法会导致计数器位数很大。后来我发现可以用两级分频来解决,比如先10分频,再100分频,这样总共就是1000分频,但每级计数器只需要很少的位数。

3. 奇数分频的两种实现方案

奇数分频(如3分频、5分频)比偶数分频复杂一些,主要难点在于如何保持50%的占空比。经过多次实践,我总结出两种可靠的实现方法。

3.1 双计数器法

第一种方法使用两个计数器,分别在时钟的上升沿和下降沿工作:

module odd_divider #( parameter DIV = 5 // 分频系数,奇数 )( input clk, input rst_n, output clk_out ); reg [3:0] cnt_p, cnt_n; // 上升沿和下降沿计数器 reg clk_p, clk_n; // 两个中间时钟 // 上升沿计数器 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_p <= 0; clk_p <= 0; end else if(cnt_p == DIV - 1) begin cnt_p <= 0; clk_p <= ~clk_p; end else begin cnt_p <= cnt_p + 1; end end // 下降沿计数器 always @(negedge clk or negedge rst_n) begin if(!rst_n) begin cnt_n <= 0; clk_n <= 0; end else if(cnt_n == DIV - 1) begin cnt_n <= 0; clk_n <= ~clk_n; end else begin cnt_n <= cnt_n + 1; end end assign clk_out = clk_p | clk_n; // 组合输出 endmodule

这种方法通过将上升沿和下降沿生成的时钟信号进行或运算,可以得到完美的50%占空比。我在一个SPI接口项目中就使用了这种5分频设计,效果很好。

3.2 半整数分频法

第二种方法更巧妙,先进行(N-1)/2 + 0.5分频,再进行2分频:

module odd_divider_alt #( parameter DIV = 7 )( input clk, input rst_n, output reg clk_out ); reg [3:0] counter; reg clk_half; // 生成 (DIV-1)/2 + 0.5 分频 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 0; clk_half <= 0; end else if(counter == DIV - 1) begin counter <= 0; clk_half <= ~clk_half; end else begin counter <= counter + 1; if(counter == (DIV-1)/2) clk_half <= ~clk_half; end end // 再进行2分频 always @(posedge clk_half or negedge rst_n) begin if(!rst_n) clk_out <= 0; else clk_out <= ~clk_out; end endmodule

这种方法代码更简洁,但理解起来需要一些技巧。我在一个音频处理项目中对比过两种方法,发现第二种方法在资源利用上更高效。

4. 任意整数分频的通用实现

在实际项目中,我们经常需要实现任意整数分频(包括奇数和偶数)。下面分享一个我经过多次优化后的通用分频器设计:

module universal_divider #( parameter DIV = 10 // 任意正整数分频系数 )( input clk, input rst_n, output reg clk_out ); reg [31:0] counter; reg clk_p, clk_n; generate if(DIV == 1) begin // 1分频特殊情况 always @(posedge clk or negedge rst_n) begin if(!rst_n) clk_out <= 0; else clk_out <= clk; end end else if(DIV[0] == 0) begin // 偶数分频 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 0; clk_out <= 0; end else if(counter == DIV/2 - 1) begin counter <= 0; clk_out <= ~clk_out; end else begin counter <= counter + 1; end end end else begin // 奇数分频 // 上升沿计数器 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 0; clk_p <= 0; end else if(counter == DIV - 1) begin counter <= 0; clk_p <= ~clk_p; end else begin counter <= counter + 1; if(counter == (DIV-1)/2) clk_p <= ~clk_p; end end // 下降沿计数器 always @(negedge clk or negedge rst_n) begin if(!rst_n) begin clk_n <= 0; end else begin if(counter == (DIV-1)/2) clk_n <= ~clk_n; end end assign clk_out = clk_p | clk_n; end endgenerate endmodule

这个设计有几个关键点:

  1. 使用generate语句根据分频系数的奇偶性选择不同的实现方式
  2. 对于偶数分频,采用简单的计数器翻转法
  3. 对于奇数分频,结合了双沿计数和半整数分频的优点
  4. 特殊处理了1分频的情况

在最近的一个多时钟域项目中,这个通用分频器帮了大忙,可以灵活地生成各种需要的时钟频率。不过要注意,这种数���分频产生的时钟可能会有较大的抖动,对于高精度时序要求的场合,建议使用PLL等模拟方式。

http://www.jsqmd.com/news/1085665/

相关文章:

  • 国内大模型与国外大模型的差距在哪里
  • 基于LLM的知识图谱自动构建系统:从非结构化数据到结构化知识的智能转换
  • 华为MSTP、Eth-Trunk、VRRP融合组网:从原理到高可用企业网实战
  • 从质点、刚体到机械臂:一文读懂自由度的物理本质与工程应用
  • CNSH 中文原生脚本实战(一):为什么中国人需要自己的脚本语言
  • 解码Android相机架构:从App到HAL的请求流转全景
  • Python高效访问B站API的终极指南:构建专业级数据采集与分析系统
  • 终极指南:如何用智能激活脚本一键搞定Windows和Office?
  • 终极Windows安卓应用安装器:告别模拟器,原生运行APK的完整指南
  • 数据库工程:Explain对比与慢查询优化实战‌
  • 基于SM4国密算法实现.NET Core大文件安全分片上传
  • PiliPlus:你的终极B站第三方客户端,打造个性化视频体验
  • 文件上传漏洞实战:从原理到防御,剖析企业应用安全风险
  • QMCDecode技术实践:三步完成QQ音乐加密格式转换的开源方案
  • JRC全球地表水动态制图:从30米像素洞察35年水资源变迁
  • 从零到一:K8S滚动更新与探针配置实战优化
  • 照着教程搭了电商AI批量出图工作流,500张图全废了
  • 技术深度解析:OpenSpeedy游戏加速工具的时间函数Hook实现方案
  • 从NOIP方格取数到双线程DP:解析经典棋盘路径问题的动态规划核心
  • 3个颠覆性技巧:如何让网盘下载体验效率翻倍?
  • 【Docker】无缝升级至Docker-CE:实战指南与数据零丢失迁移策略
  • UE特效实战:打造动态武器附魔光效
  • 终极指南:如何用开源工具获取网盘直链下载地址,突破下载限制
  • 华为网络设备ARP安全防护实战:从基础限速到高级检测
  • SEGGER_RTT_printf()扩展浮点与负数打印-嵌入式调试实战
  • Outfit字体:9种字重开源几何字体助力品牌设计高效实现
  • 线上扭蛋一番赏系统搭建通俗解析:不用硬核技术词,直白讲清商家刚需与落地实际收益
  • Windows字体渲染优化终极指南:3分钟掌握Better ClearType Tuner
  • 【实战】LIO_SAM与KITTI 08数据集:从数据对齐到轨迹评估全解析
  • Elsevier Tracker:3步实现Elsevier投稿状态实时追踪,科研效率提升90%