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

最近在研究异步fifo的同步化设计,里面需要格雷码与二进制码的转换,因此研究了下实现的代码。说起来这次的代码体验真的是很有意思,简单的几行代码,先后提高了自己三次认识,收益匪浅啊。


先看下两种码的转化原理:格雷码的特点就是相邻两个数之间只有一位不同,就不细说了,主要看转换。二进制与格雷码的对照关系如下:

二进制格雷码二进制格雷码
0000000010001100
0001000110011101
0010001110101111
0011001010111110
0100011011001010
0101011111011011
0110010111101001
0111010011111000


二者之间的转换关系各种资料中也说的很多了,简单而言就是:

二进制转格雷码,二进制最高位为格雷码最高位,二进制最高位与二进制次高位相亦或为格雷码次高位,以后二进制各位均如此得到(或者简单的记为二进制最高位前补一个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<width; i=i+1)
        data_out_G_n[i] = data_in_B_exp[i] ^ data_in_B_exp[i+1];
end

reg [width-1: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_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


  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尼德兰的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值