格雷码(Gray code)是由贝尔实验室的Frank Gray在1940年提出,用于在PCM(Pusle Code Modulation)方法传送讯号时防止出错,并于1953年三月十七日取得美国专利。格雷码是一个数列集合,相邻两数间只有一个位元改变,为无权数码,且格雷码的顺序不是唯一的。
直接排列
以二进制为0值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反覆,即可排列出n个位元的格雷码。
二进制码->格雷码(编码):从最右边一位起,依次将每一位与左边一位异或(XOR),作为对应格雷码该位的值,最左边一位不变(相当于左边是0);
格雷码-〉二进制码(解码):从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变).
数学(计算机)描述:
原码:p[n:0];格雷码:c[n:0](n∈N);编码:c=G(p);解码:p=F(c);
书写时按从左向右标号依次减小,即MSB->LSB,编解码也按此顺序进行
编码:
...................c[n]=p[n],
...................c=p XOR p[i+1] (i∈N,n-1≥i≥0);
解码:
...................p[n]=c[n],
...................P=c XOR p[i+1] (i∈N, n-1≥i≥0)。 //注意
二进制数转格雷码
(假设以二进制为0的值做为格雷码的0)
格雷码第n位 = 二进制码第(n+1)位+二进制码第n位。不必理会进制。
Verilog 代码:gray=(binary>>1)^binary;
格雷码转二进制数
二进制码第n位 = 二进制码第(n+1)位+格雷码第n位。因为二进制码和格雷码皆有相同位数,所以二进制码可从最高位的左边位元取0,以进行计算。
verilog 代码://------假设 reg [n-1] gray,binary;
integer i;
for(i=0;i<=n-1;i=i+1)
binary=
^(gray>>i)//gray移位后,自身按位异或
^(gray>>i)//第一次看到这样的用法,特意查了下verilog语法
方法一
代码:
module gray
#(parameter N=5)
(
input clk,
input rst_n,
output reg [N-1:0] gray_cnt_out
);
reg [N-1:0] cnt;
reg [N-1:0] temp;
integer i;
always @ (posedge clk ,negedge rst_n)
begin
if(!rst_n)
cnt <= 5'd0;
else
cnt <= cnt +1'b1;
end
always @(cnt)
begin
temp[N-1] = cnt[N-1];
for(i=1; i<=N-1;i=i+1)
temp[i-1] = cnt[i-1]^cnt;
end
// 此处for循环综合会展开 不推荐这种写法
always @ (posedge clk ,negedge rst_n)
begin
if(!rst_n)
gray_cnt_out<=1'b0;
else
gray_cnt_out<=temp;
end
endmodule
备注:在实际工程中,Gray码计数器广泛应用于跨时钟域设计,他用于将计数器的计数值从A时钟域传递给B时钟域。由于信号的传播延时,而且CLKA和CLKB没有固定的相位关系,CLKA_cnt的输出不能同时到达CLKB,如果用二进制计数器,则可能出现毛刺,从而导致设计错误;而如果采样Gray码计数器,根据Gray的特性,则可以有效地避免这个问题。
方法2:(只有一个寄存器的gray计数器)
代码:
module gray_counter
(
input iclk,
input irst_n,
input ivalid,
output reg [3:0] gray
);
wire [3:0] bin_counter;
wire [3:0] gray_counter;
reg [3:0] G2B_counter;
always @(*)
begin
G2B_counter[3] = gray[3];
G2B_counter[2] = gray[2] ^ G2B_counter[3];
G2B_counter[1] = gray[1] ^ G2B_counter[2];
G2B_counter[0] = gray[0] ^ G2B_counter[1];
end
assign bin_counter = bin_counter +ivalid;
assign gray_counter = (bin_counter >>1) ^ bin_counter;
//理解该处的电路结构
always @( posedge iclk or negedge irst_n)
begin
if (!irst_n)
begin
gray <= 4'b0;
end
else
begin
gray <= gray_counter;
end
end
endmodule
// 为码
always @(gray) begin for (i=0; i<`WIDTH; i=i+1) bin = ^(gray>>i); // Gray码到二进制码转换 bnext = bin + 1; gnext = (bnext>>1) ^ bnext; // 二进制码到Gray码转换 end
|
方法3(提高频率,增加寄存器,减少组合逻辑)
module
graycounter
(
input
iclk,
input
irst_n,
input
ivalid,
output
[ADDSIZE-1 : 0] bin,
output
reg
[ADDSIZE : 0] gray
);
parameter ADDSIZE = 4;
wire
[ADDSIZE : 0] binnext;
wire
[ADDSIZE : 0] graynext;
reg
[ADDSIZE : 0] bin_o;
assign
binnext = bin_o + ivalid;
assign
graynext = (binnext >>1) ^ binnext;
assign
bin = bin_o[ADDSIZE-1 : 0];
always
@(
posedge
iclk
or
negedge
irst_n )
if
(!irst_n)
{bin_o, gray} <= 0;
else
{bin_o, gray} <= {binnext, graynext};
endmodule