最近在研究异步fifo的同步化设计,里面需要格雷码与二进制码的转换,因此研究了下实现的代码。说起来这次的代码体验真的是很有意思,简单的几行代码,先后提高了自己三次认识,收益匪浅啊。
先看下两种码的转化原理:格雷码的特点就是相邻两个数之间只有一位不同,就不细说了,主要看转换。二进制与格雷码的对照关系如下:
二进制 | 格雷码 | 二进制 | 格雷码 |
0000 | 0000 | 1000 | 1100 |
0001 | 0001 | 1001 | 1101 |
0010 | 0011 | 1010 | 1111 |
0011 | 0010 | 1011 | 1110 |
0100 | 0110 | 1100 | 1010 |
0101 | 0111 | 1101 | 1011 |
0110 | 0101 | 1110 | 1001 |
0111 | 0100 | 1111 | 1000 |
二者之间的转换关系各种资料中也说的很多了,简单而言就是:
二进制转格雷码,二进制最高位为格雷码最高位,二进制最高位与二进制次高位相亦或为格雷码次高位,以后二进制各位均如此得到(或者简单的记为二进制最高位前补一个0,然后依次与后面二进制求亦或得到格雷码)。
格雷码转二进制,二进制最高位为格雷码最高位,二进制最高位与格雷码次高位相亦或为二进制次高位,以后格雷码各位均如此得到(或者简单的记为二进制最高位前补一个0,然后依次与后面格雷码求亦或得到二进制)。
用图表示如下:
1.第一次代码
第一次写我想就写一个8位的吧,毕竟8位的数一般也够用了,所以依照上图我就把东西直接铺开了写的,后来一看惨不忍睹。依照此图首先进行了8位输入输出的设计,二进制转格雷码
module B2G(
clk,
rst,
data_in_B,
data_out_G
);
input clk, rst;
input [7:0]data_in_B;
output [7:0]data_out_G;
wire [7:0]data_out_G_n;
wire [8:0]data_in_B_exp;
assign data_in_B_exp = {1'b0, data_in_B};
assign data_out_G_n[0] = data_in_B_exp[0] ^ data_in_B_exp[1];
assign data_out_G_n[1] = data_in_B_exp[1] ^ data_in_B_exp[2];
assign data_out_G_n[2] = data_in_B_exp[2] ^ data_in_B_exp[3];
assign data_out_G_n[3] = data_in_B_exp[3] ^ data_in_B_exp[4];
assign data_out_G_n[4] = data_in_B_exp[4] ^ data_in_B_exp[5];
assign data_out_G_n[5] = data_in_B_exp[5] ^ data_in_B_exp[6];
assign data_out_G_n[6] = data_in_B_exp[6] ^ data_in_B_exp[7];
assign data_out_G_n[7] = data_in_B_exp[7] ^ data_in_B_exp[8];
reg [7:0]data_out_G;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
data_out_G <= 0;
end
else begin
data_out_G <= data_out_G_n;
end
end
endmodule
格雷码转二进制
module G2B(
clk,
rst,
data_in_G,
data_out_B
);
input clk, rst;
input [7:0]data_in_G;
output [7:0]data_out_B;
wire [7:0]data_out_B_n;
wire [8:0]data_in_B_exp;
assign data_in_B_exp = {1'b0, data_out_B_n};
assign data_out_B_n[0] = data_in_G[0] ^ data_in_B_exp[1];
assign data_out_B_n[1] = data_in_G[1] ^ data_in_B_exp[2];
assign data_out_B_n[2] = data_in_G[2] ^ data_in_B_exp[3];
assign data_out_B_n[3] = data_in_G[3] ^ data_in_B_exp[4];
assign data_out_B_n[4] = data_in_G[4] ^ data_in_B_exp[5];
assign data_out_B_n[5] = data_in_G[5] ^ data_in_B_exp[6];
assign data_out_B_n[6] = data_in_G[6] ^ data_in_B_exp[7];
assign data_out_B_n[7] = data_in_G[7] ^ data_in_B_exp[8];
reg [7:0]data_out_B;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
data_out_B <= 0;
end
else begin
data_out_B <= data_out_B_n;
end
end
endmodule
怎么样是不是肯定没出错,那是必然的,这么铺开了写咋能出错。
2.第二次代码
后来一想这样不行,我这个设计输入大于8位或者是任意位的话会出错,不具有通用性。而且这样铺开了写肯定不是办法,要用for循环来展开就对了。所以又查了下资料,写了第二版代码。任意小于32位宽的输入输出设计(integer型是32位),二进制转格雷码:
module B2G_new #(
parameter width = 8
)(
clk,
rst,
data_in_B,
data_out_G
);
input clk, rst;
input [width-1:0]data_in_B;
output [width-1:0]data_out_G;
reg [width-1:0]data_out_G_n;
wire [width:0]data_in_B_exp;
assign data_in_B_exp = {1'b0, data_in_B};
integer i;
always @(data_in_B) begin
for(i=0; i
格雷码转二进制:
module G2B_new #(
parameter width = 8
)(
clk,
rst,
data_in_G,
data_out_B
);
input clk, rst;
input [width-1:0]data_in_G;
output [width-1:0]data_out_B;
reg [width-1:0]data_out_B_n;
integer i;
always @(data_in_G or posedge rst) begin
data_out_B_n[width-1] = data_in_G[width-1];
for(i=width-1; i>0; i=i-1)
data_out_B_n[i-1] = data_in_G[i-1] ^ data_out_B_n[i];
end
reg [width-1:0]data_out_B;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
data_out_B <= 0;
end
else begin
data_out_B <= data_out_B_n;
end
end
endmodule
写晚这个时候觉得还行,然后我手残的打开了书,看到了书上的写法,意识到了自己的差距。。。
3.第三次代码
书上的代码太简单了,而且里面还有思维转换的过程。
二进制转格雷码:
module B2G_Conv #(parameter size = 4)(
output [size-1:0] gray,
input [size-1:0] binary
);
assign gray = (binary >> 1) ^ binary;
endmodule
module G2B_Conv #(parameter size = 4)(
output reg [size-1:0] binary,
input [size-1:0] gray
);
integer k;
always @(gray)
begin
for (k = 0; k < size; k = k + 1)
binary[k] = ^(gray >> k);
end
endmodule
也就是说二进制转格雷码就是把二进制错位下然后按位亦或(果然如此),格雷码转二进制就是就是把当前位(包括当前的第k位)以前的所有格雷码按位亦或就好了,一观察果然是,厉害了。
我们再把转换那个图拿来看看,一看果然是对的。后面异步FIFO的设计就是用的这个代码。
附:
顶层模块如下(没有第三次代码的加入):
module work(
clk,
rst
);
input clk, rst;
reg [7:0]data_in_B;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
data_in_B <= 0;
end
else begin
data_in_B <= data_in_B + 1;
end
end
wire [7:0]data_out_G1, data_out_G2, data_out_B1, data_out_B2;
B2G u_b2g(
.clk(clk),
.rst(rst),
.data_in_B(data_in_B),
.data_out_G(data_out_G1)
);
B2G_new u_b2g_new(
.clk(clk),
.rst(rst),
.data_in_B(data_in_B),
.data_out_G(data_out_G2)
);
G2B u_g2b(
.clk(clk),
.rst(rst),
.data_in_G(data_out_G1),
.data_out_B(data_out_B1)
);
G2B_new u_g2b_new(
.clk(clk),
.rst(rst),
.data_in_G(data_out_G2),
.data_out_B(data_out_B2)
);
endmodule
testbench如下:
module tb();
reg clk;
reg rst;
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
initial begin
rst = 1;
#24 rst = 0;
end
work u(
.clk(clk),
.rst(rst)
);
endmodule