目录
格雷码介绍
转化原理
Verilog 实现
testbench 测试代码
仿真波形
格雷码介绍
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。在数字系统中,常要求代码按一定顺序变化。例如,按自然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定情况下可能导致电路状态错误或输入错误。使用格雷码可以避免这种错误。
在常见的 IP 设计中就会用到格雷码,比如异步 FIFO 的实现,在读时钟域同步到写时钟域或写时钟域同步到读时钟域时,就需要将数据由二进制码转化为格雷码,转化后的数据响相邻数据之间只有一位不同,这样就大大降低了数据同步时出错的概率。
以下为四位二进制码和格雷码之间的对应关系:
Decimal |
Binary |
Gray Code |
0 |
0000 |
0000 |
1 |
0001 |
0001 |
2 |
0010 |
0011 |
3 |
0011 |
0010 |
4 |
0100 |
0110 |
5 |
0101 |
0111 |
6 |
0110 |
0101 |
7 |
0111 |
0100 |
8 |
1000 |
1100 |
9 |
1001 |
1101 |
10 |
1010 |
1111 |
11 |
1011 |
1110 |
12 |
1100 |
1010 |
13 |
1101 |
1011 |
14 |
1110 |
1001 |
15 |
1111 |
1000 |
可以很明显的看出,随着数值增大,格雷码相邻数之间的差别只有一位之差,下面介绍二进制码和格雷码之间是如何互相转化的。
转化原理
二进制码转格雷码
如下图所示,二进制码转格雷码,格雷码的最高位和二进制的最高位一致,次高位为二进制的最高位和次高位的异或结果,同理可得格雷码最低位为二进制码的第1位和第0位的异或结果。
格雷码转二进制码
如下图所示,格雷码转二进制码,二进制码的最高位和格雷码的最高位一致,次高位为格雷码的最高位和二进制码的次高位的异或结果,同理可得格雷码最低位为格雷码的第1位和二进制码的第0位的异或结果。
Verilog 实现
二进制码转格雷码
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer : Linest-5
/* File : bin2gray.v
/* Create : 2022-08-30 15:35:07
/* Revise : 2022-08-30 15:35:07
/* Module Name : bin2gray
/* Description : 二进制码转格雷码
/* Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
module bin2gray#(
parameter DATA_WIDTH = 'd8
)(
input [DATA_WIDTH-1:0] bin_code,
output [DATA_WIDTH-1:0] gray_code
);
generate
genvar i;
for (i=0;i<DATA_WIDTH-1;i=i+1) begin
assign gray_code[i] = bin_code[i+1] ^ bin_code[i];
end
endgenerate
assign gray_code[DATA_WIDTH-1] = bin_code[DATA_WIDTH-1];
endmodule
格雷码转二进制码
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer : Lqc
/* File : gray2bin.v
/* Create : 2022-08-30 15:09:27
/* Revise : 2022-08-30 15:09:27
/* Module Name : gray2bin
/* Description : 格雷码转二进制码
/* Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
module gray2bin#(
parameter DATA_WIDTH = 'd8
)(
input [DATA_WIDTH-1:0] gray_code,
output [DATA_WIDTH-1:0] bin_code
);
generate
genvar i;
for (i=0;i<DATA_WIDTH-1;i=i+1) begin
assign bin_code[i] = bin_code[i+1] ^ gray_code[i];
end
endgenerate
assign bin_code[DATA_WIDTH-1] = gray_code[DATA_WIDTH-1];
endmodule
testbench 测试代码
将两个模块都例化到测试模块中,输入二进制码转化成格雷码,然后再转化回二进制码,以此来验证两个模块的设计正确性。
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer : Lqc
/* File : tb_bin_gray.v
/* Create : 2022-08-30 15:40:10
/* Revise : 2022-08-30 15:40:10
/* Module Name : tb_bin_gray
/* Description : 二进制码和格雷码互转仿真模块
/* Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns/1ps
module tb_bin_gray();
reg [7:0] bin_code_in;
wire [7:0] gray_code_out;
wire [7:0] bin_code_out;
//每10个单位时间输入的数据加1
integer i;
initial begin
for (i=0;i<256;i=i+1) begin
bin_code_in = #20 i;
end
end
//例化二进制码转格雷码模块
bin2gray #(
.DATA_WIDTH(8)
) inst_bin2gray
(
.bin_code(bin_code_in),
.gray_code(gray_code_out)
);
//例化格雷码转二进制码模块
gray2bin #(
.DATA_WIDTH(8)
) inst_gray2bin
(
.gray_code(gray_code_out),
.bin_code(bin_code_out)
);
endmodule
仿真波形
依次输入8位二进制数据 0-255,可以看到格雷码相邻的数据都只变化一位,并且重新转化为二进制码的数据也和输入的数据一致,设计正确!