格雷码:Gray Code
更新时间:2026-09-21。芯片设计进阶。应用见 异步 FIFO CDC,理论见 亚稳态 MTBF。
一句话回答
格雷码的特点是相邻两个数只有 1 个 bit 不同;多 bit 的计数指针跨时钟域时,如果用普通二进制(可能多位同时翻转),采样到各 bit 跳变时刻不一致就会拼出一个完全错误的值;换成格雷码指针,哪怕恰好在跳变时被另一时钟域采样,也只会读到「旧值」或「新值」,不会读到第三种非法值,所以异步 FIFO 的读写指针都先转成格雷码再跨域同步。
一、对比
bash
十进制 二进制 格雷码
0 000 000
1 001 001 ← 只变1位
2 010 011
3 011 010
...
注意二进制 3→4:011→100,三位同时翻转!多位同时翻转时,各 bit 的微小延迟差使目标域可能采到 010、001…任意中间组合。
二、转换公式
bash
gray = binary ^ (binary >> 1)
binary[i] = ^(gray[MSB:i]) // 二进制第i位 = 格雷码从最高位到该位的异或systemverilog
// 二进制 -> 格雷码
assign gray = bin ^ (bin >> 1);
// 格雷码 -> 二进制
always_comb begin
bin[MSB] = gray[MSB];
for (int i = MSB-1; i >= 0; i--)
bin[i] = bin[i+1] ^ gray[i];
end三、在异步 FIFO 里
- 读写地址指针用二进制做存储器寻址和实际计数;
- 跨域前转成格雷码,再用同步器打到对方时钟域;
- 对方拿到的指针保证是旧值或新值,据此保守判断 空/满;
- 判空判满会略微保守(最多差一个位置),换来不会误判的安全性。
常见坑
- 直接同步二进制指针:多位同时翻转,偶发读到错误地址,极难调试。
- 忘记格雷码只能相邻单 bit:它只适合单调递增/递减、且每次 +1 的指针;任意跳变的普通数据不能靠格雷码跨域。
- 空满比较用错位宽:异步 FIFO 指针通常多用 1 位来区分「空一圈/满一圈」。
- 格雷码指针仍需打拍同步:单 bit 变化降低「读到非法值」风险,但亚稳态本身仍由同步器 + MTBF 保证。
- 转换公式写错:二进制转格雷码简单(异或右移),反向要逐位累积异或。
- 用格雷码传多 bit 数据总线:数据不是连续指针,不满足单 bit 跳变,应走握手或 FIFO。
总结
格雷码相邻值只有 1 bit 变化,使异步 FIFO 的计数指针跨时钟域时即使采样到跳变点也只会取到旧值或新值,杜绝多位同时翻转导致的非法中间值;二进制先转格雷码(bin ^ bin>>1)再同步、对方转回做保守的空满判断;它只适用于单调连续的指针,不能用来传普通数据。