news 2026/4/22 7:44:21

Verilog实战:5分钟搞定Johnson计数器(附完整代码与仿真测试)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Verilog实战:5分钟搞定Johnson计数器(附完整代码与仿真测试)

Verilog实战:5分钟搞定Johnson计数器(附完整代码与仿真测试)

在数字电路设计中,计数器是最基础也最常用的模块之一。Johnson计数器以其独特的环形结构和高效的资源利用率,成为许多FPGA项目中的首选方案。不同于普通的二进制计数器,Johnson计数器只需要n个触发器就能产生2n个状态序列,这种特性使其在需要长周期计数但资源有限的场景中尤为珍贵。

对于FPGA初学者来说,理解Johnson计数器的工作原理固然重要,但更重要的是能够快速实现并验证其功能。本文将从一个工程师的实际开发角度出发,带你快速搭建一个可工作的Johnson计数器模块,并提供完整的测试方案。无论你是正在学习数字电路的学生,还是需要快速实现特定功能的工程师,这篇文章都能为你提供即插即用的解决方案。

1. Johnson计数器核心原理与优势

Johnson计数器,又称扭环计数器,是一种特殊的移位寄存器结构。它的核心思想是将最后一级触发器的输出取反后反馈到第一级的输入。这种简单的改动带来了显著的性能提升:

  • 状态利用率翻倍:n级Johnson计数器可以产生2n个有效状态,而普通环形计数器只有n个
  • 自校正特性:即使因为干扰进入非法状态,也能在几个时钟周期内自动恢复
  • 低功耗设计友好:状态变化每次只有一位翻转,减少了动态功耗

以一个4位Johnson计数器为例,它的状态循环如下:

0000 → 1000 → 1100 → 1110 → 1111 → 0111 → 0011 → 0001 → 0000

这种独特的"波浪形"状态变化模式,使其在旋转编码器、序列检测器等应用中表现出色。

2. Verilog实现方案对比

2.1 移位寄存器实现(推荐)

这是最简洁高效的实现方式,仅需几行代码即可完成:

module johnson_counter #(parameter WIDTH=4) ( input clk, input rst, output reg [WIDTH-1:0] count ); always @(posedge clk or posedge rst) begin if (rst) count <= 0; else count <= {~count[0], count[WIDTH-1:1]}; end endmodule

关键点解析

  • WIDTH参数允许灵活调整计数器位数
  • 复位信号rst确保计数器从全0状态开始
  • 核心操作{~count[0], count[WIDTH-1:1]}实现了取反反馈和移位

2.2 状态机实现(教学用途)

虽然代码量较大,但有助于理解计数器的工作原理:

module johnson_counter_fsm ( input clk, input rst, output reg [3:0] count ); parameter S0=0, S1=1, S2=2, S3=3, S4=4, S5=5, S6=6, S7=7; reg [2:0] state, next_state; // 状态寄存器 always @(posedge clk or posedge rst) begin if (rst) state <= S0; else state <= next_state; end // 下一状态逻辑 always @(*) begin case(state) S0: next_state = S1; S1: next_state = S2; S2: next_state = S3; S3: next_state = S4; S4: next_state = S5; S5: next_state = S6; S6: next_state = S7; S7: next_state = S0; default: next_state = S0; endcase end // 输出逻辑 always @(*) begin case(state) S0: count = 4'b0000; S1: count = 4'b1000; S2: count = 4'b1100; S3: count = 4'b1110; S4: count = 4'b1111; S5: count = 4'b0111; S6: count = 4'b0011; S7: count = 4'b0001; default: count = 4'b0000; endcase end endmodule

提示:实际项目中推荐使用移位寄存器实现,状态机版本更适合教学演示和原理验证。

3. 测试平台搭建与功能验证

一个完整的计数器设计必须包含充分的验证环节。下面是一个完整的测试平台示例:

`timescale 1ns/1ps module tb_johnson_counter; reg clk; reg rst; wire [3:0] count; // 实例化被测模块 johnson_counter uut ( .clk(clk), .rst(rst), .count(count) ); // 时钟生成(100MHz) initial begin clk = 0; forever #5 clk = ~clk; end // 测试流程 initial begin // 初始化 rst = 1; #20 rst = 0; // 观察8个完整周期(64个时钟边沿) #640; // 测试复位功能 rst = 1; #20; if (count != 4'b0000) $display("复位测试失败!"); else $display("复位测试通过"); // 结束仿真 $finish; end // 状态监控 always @(posedge clk) begin $display("时间:%t, 计数器值:%b", $time, count); end endmodule

测试要点

  1. 验证状态序列是否正确循环
  2. 检查复位功能是否正常
  3. 观察状态转换是否发生在正确的时钟边沿
  4. 确认计数器能否从非法状态自动恢复

4. 常见问题排查指南

在实际项目中,Johnson计数器可能会遇到以下典型问题:

4.1 状态跳变异常

症状:计数器未按预期序列变化,或卡在某个状态

排查步骤

  1. 检查时钟信号是否正常到达所有触发器
  2. 确认复位信号在初始化时有效
  3. 验证移位方向是否符合设计(MSB先移入还是LSB先移入)
  4. 检查取反操作是否应用在正确的位上

4.2 时序违规

症状:在高速时钟下计数器工作不稳定

解决方案

  • 添加流水线寄存器
  • 降低时钟频率
  • 检查FPGA布局布线报告,优化关键路径
// 添加一级流水线的改进版本 always @(posedge clk or posedge rst) begin if (rst) begin count <= 0; feedback <= 0; end else begin feedback <= ~count[0]; count <= {feedback, count[WIDTH-1:1]}; end end

4.3 仿真与硬件行为不一致

可能原因

  • 仿真中的时钟抖动与真实硬件不同
  • 异步复位信号在硬件中未正确处理
  • FPGA工具链优化掉了关键逻辑

调试技巧

  1. 在仿真中添加更多的调试输出
  2. 使用SignalTap或ChipScope等嵌入式逻辑分析仪
  3. 逐步降低优化级别进行对比测试

5. 进阶应用与性能优化

掌握了基本实现后,Johnson计数器还可以进一步优化以适应更复杂的应用场景:

5.1 可配置方向控制

通过添加方向控制信号,可以实现双向计数:

module johnson_counter_dir #(parameter WIDTH=4) ( input clk, input rst, input dir, // 0=正向,1=反向 output reg [WIDTH-1:0] count ); always @(posedge clk or posedge rst) begin if (rst) count <= 0; else if (dir == 0) count <= {~count[0], count[WIDTH-1:1]}; // 正向 else count <= {count[WIDTH-2:0], ~count[WIDTH-1]}; // 反向 end endmodule

5.2 多相时钟生成

利用Johnson计数器的特性,可以产生多相时钟信号:

// 生成4相不重叠时钟 assign phase0 = count[3] & ~count[2]; assign phase1 = count[3] & count[2]; assign phase2 = ~count[3] & count[2]; assign phase3 = ~count[3] & ~count[2];

5.3 低功耗优化

针对便携式设备的优化策略:

  • 使用时钟门控技术
  • 采用独热码编码减少状态切换功耗
  • 在空闲时段关闭计数器时钟
// 带时钟门控的实现 always @(posedge clk or posedge rst) begin if (rst) begin count <= 0; clk_en <= 0; end else if (enable) begin clk_en <= 1; count <= {~count[0], count[WIDTH-1:1]}; end else begin clk_en <= 0; end end assign gated_clk = clk & clk_en;

在最近的一个电机控制项目中,我们使用4位Johnson计数器成功实现了步进电机的微步控制,仅用少量逻辑资源就完成了原本需要复杂状态机的功能。这种简洁而高效的设计,正是FPGA开发的魅力所在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 7:42:01

3分钟搞定!PotPlayer AI字幕翻译插件终极使用指南

3分钟搞定&#xff01;PotPlayer AI字幕翻译插件终极使用指南 【免费下载链接】PotPlayer_Subtitle_Translate_Baidu PotPlayer 字幕在线翻译插件 - 百度平台 项目地址: https://gitcode.com/gh_mirrors/po/PotPlayer_Subtitle_Translate_Baidu 还在为看不懂的外语视频发…

作者头像 李华
网站建设 2026/4/22 7:41:56

CefFlashBrowser终极指南:让消失的Flash世界重现生机

CefFlashBrowser终极指南&#xff1a;让消失的Flash世界重现生机 【免费下载链接】CefFlashBrowser Flash浏览器 / Flash Browser 项目地址: https://gitcode.com/gh_mirrors/ce/CefFlashBrowser 你是否还记得那些经典的Flash游戏&#xff1f;那些曾经在4399、7K7K等网站…

作者头像 李华
网站建设 2026/4/22 7:39:46

自动化扩展:应对流量洪峰的 Agent Harness

自动化扩展:应对流量洪峰的 Agent Harness 1. 标题 (Title) 智能驱动的云原生弹性:深入理解 Agent Harness 自动扩展架构 告别被动响应!Agent Harness 如何让你的系统主动迎接流量洪峰 从“手忙脚乱”到“运筹帷幄”:基于 Agent Harness 的自动化扩展实战指南 下一代自动扩…

作者头像 李华
网站建设 2026/4/22 7:34:34

TVA动态自适应注意力在光伏接线盒装配检查中的应用

前沿技术背景介绍&#xff1a;AI 智能体视觉检测系统&#xff08;Transformer-based Vision Agent&#xff0c;缩写&#xff1a;TVA&#xff09;&#xff0c;是依托 Transformer 架构与“因式智能体”范式所构建的高精度智能体。它区别于传统机器视觉与早期 AI 视觉&#xff0c…

作者头像 李华