进阶之路——二进制与格雷码的相互转换模块设计

最近在研究异步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张图片

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

进阶之路——二进制与格雷码的相互转换模块设计_第2张图片


格雷码转二进制

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
进阶之路——二进制与格雷码的相互转换模块设计_第3张图片

怎么样是不是肯定没出错,那是必然的,这么铺开了写咋能出错。


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
进阶之路——二进制与格雷码的相互转换模块设计_第4张图片

格雷码转二进制:

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
进阶之路——二进制与格雷码的相互转换模块设计_第5张图片

写晚这个时候觉得还行,然后我手残的打开了书,看到了书上的写法,意识到了自己的差距。。。


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的设计就是用的这个代码。
进阶之路——二进制与格雷码的相互转换模块设计_第6张图片


附:

顶层模块如下(没有第三次代码的加入):

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


你可能感兴趣的:(芯片前端设计,verilog)