从扭环计数器到CDC:一个被遗忘的格雷码应用,如何优雅解决状态机跨时钟域

张开发
2026/4/6 23:36:00 15 分钟阅读

分享文章

从扭环计数器到CDC:一个被遗忘的格雷码应用,如何优雅解决状态机跨时钟域
从扭环计数器到跨时钟域同步格雷码的优雅传承与工程实践在数字电路设计的漫长演进中有些经典设计思想如同陈年佳酿历久弥新。格雷码Gray Code正是这样一个存在——从1947年弗兰克·格雷发明它用于脉冲编码通信到现代超大规模集成电路中的跨时钟域同步这种独特的编码方式始终闪耀着智慧的光芒。当我们面对时钟域隔离带来的亚稳态难题时格雷码提供了一种既符合数字电路底层特性又具备数学美感的解决方案。1. 历史回眸扭环计数器中的格雷码智慧扭环计数器Johnson Counter作为移位寄存器的经典应用完美展现了格雷码的特性。这种计数器通过将最后一级寄存器的反相输出反馈到第一级输入实现了一个有趣的状态循环。以4位扭环计数器为例module johnson_counter #(parameter WIDTH4) ( input clk, rst, output reg [WIDTH-1:0] q ); always (posedge clk or posedge rst) begin if (rst) q 0; else q {~q[0], q[WIDTH-1:1]}; end endmodule这个简单的电路会产生如下状态序列0000 → 1000 → 1100 → 1110 → 1111 → 0111 → 0011 → 0001 → 0000...仔细观察这个序列我们会发现它实际上构成了一个特殊的格雷码变体——相邻状态间只有一位发生变化。这种特性使得扭环计数器在早期数字系统中大放异彩毛刺消除组合逻辑解码时不会出现多比特同时跳变导致的毛刺功耗优化每次状态转换只有一位寄存器翻转降低动态功耗可靠性提升消除了信号偏移skew导致的瞬态错误提示现代FPGA设计中扭环计数器仍常用于低功耗状态机和环形振荡器设计。2. 跨时钟域同步的本质挑战当数字系统发展到多时钟域架构时工程师们遇到了一个棘手的物理限制亚稳态Metastability。这种现象发生在当时钟边沿采样异步信号时寄存器输出可能在一段时间内处于不确定的中间电平状态。虽然使用两级触发器同步器可以降低亚稳态传播概率module sync_2ff #(parameter WIDTH1) ( input clk, rst, input [WIDTH-1:0] async_in, output [WIDTH-1:0] sync_out ); reg [WIDTH-1:0] stage1, stage2; always (posedge clk or posedge rst) begin if (rst) {stage2, stage1} 0; else {stage2, stage1} {stage1, async_in}; end assign sync_out stage2; endmodule但对于多比特信号同步器只能保证每个比特最终稳定不能保证所有比特在同一周期稳定。考虑一个4位二进制计数器从0111跳变到1000时跨越时钟域时钟域A可能被时钟域B采样的中间状态01110111 (所有位保持)→10001111 (所有位同时变化时采样)0000 (最坏情况)1011 (部分位变化)这种多比特变化导致的数据扭曲Data Corruption问题使得传统同步器对多比特总线完全失效。我们需要一种编码方式确保无论何时采样数据变化都只涉及单比特跳变——这正是格雷码的核心价值。3. 格雷码的数学特性与硬件实现格雷码的精妙之处在于其数学构造。与普通二进制编码不同格雷码采用递归反射法生成1位格雷码0, 1n1位格雷码前2ⁿ个码字是在n位格雷码前加0后2ⁿ个码字是在n位格雷码的镜像前加1这种构造方法保证了相邻码字的汉明距离恒为1。在硬件实现上二进制与格雷码的转换异常高效二进制转格雷码并行前缀操作assign gray (binary 1) ^ binary;格雷码转二进制级联异或module gray2bin #(parameter WIDTH4) ( input [WIDTH-1:0] gray, output [WIDTH-1:0] binary ); assign binary[WIDTH-1] gray[WIDTH-1]; generate for (genvar iWIDTH-2; i0; i--) begin assign binary[i] binary[i1] ^ gray[i]; end endgenerate endmodule实际工程中我们常用查找表LUT实现格雷码转换。以下是Xilinx FPGA中的推荐实现方式// 使用SRL16E实现高效格雷码转换 module gray_convert #(parameter WIDTH4) ( input clk, input [WIDTH-1:0] din, output [WIDTH-1:0] dout ); (* srl_style srl *) reg [WIDTH-1:0] shift_reg; always (posedge clk) begin shift_reg {shift_reg[WIDTH-2:0], din}; end assign dout (shift_reg 1) ^ shift_reg; endmodule4. 状态机设计中的格雷码实践将格雷码应用于有限状态机FSM设计时我们需要特别注意状态编码策略。传统FSM编码方式比较编码类型状态数典型应用场景跨时钟域适用性二进制2ⁿ资源受限设计差One-hotn高速状态机极差格雷码2ⁿ跨时钟域状态同步优Johnson2n低功耗循环状态机良一个典型的格雷码编码状态机实现如下module gray_fsm ( input clk, rst, input cmd, output reg [1:0] state ); // 格雷码状态定义 localparam IDLE 2b00; localparam START 2b01; localparam WORK 2b11; localparam DONE 2b10; always (posedge clk or posedge rst) begin if (rst) state IDLE; else case(state) IDLE: state cmd ? START : IDLE; START: state WORK; WORK: state (cmd some_condition) ? DONE : WORK; DONE: state IDLE; default: state IDLE; endcase end endmodule在跨时钟域状态同步时我们采用三级同步链确保可靠性源时钟域状态寄存器使用格雷码编码同步级两级格雷码同步器避免亚稳态目的时钟域格雷码转二进制解码module cdc_fsm_sync #(parameter WIDTH2) ( input src_clk, dst_clk, rst, input [WIDTH-1:0] src_state, output [WIDTH-1:0] dst_state ); // 源时钟域寄存器 reg [WIDTH-1:0] src_reg; always (posedge src_clk or posedge rst) begin if (rst) src_reg 0; else src_reg src_state; end // 格雷码同步链 (* ASYNC_REG TRUE *) reg [WIDTH-1:0] sync0, sync1, sync2; always (posedge dst_clk or posedge rst) begin if (rst) {sync2, sync1, sync0} 0; else {sync2, sync1, sync0} {sync1, sync0, src_reg}; end // 格雷码转二进制 gray2bin #(WIDTH) converter(.gray(sync2), .bin(dst_state)); endmodule实际项目中我曾遇到一个视频处理芯片的案例图像处理单元200MHz需要将帧状态同步到显示控制器148.5MHz。最初使用二进制编码导致每千帧出现1-2次状态错误改用格雷码编码后连续测试72小时零错误。这个经验印证了格雷码在跨时钟域设计中的可靠性优势。

更多文章