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

来源:互联网 发布:hbase快速清空表数据 编辑:程序博客网 时间:2024/05/16 15:16

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


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

二进制格雷码二进制格雷码00000000100011000001000110011101001000111010111100110010101111100100011011001010010101111101101101100101111010010111010011111000


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

二进制转格雷码,二进制最高位为格雷码最高位,二进制最高位与二进制次高位相亦或为格雷码次高位,以后二进制各位均如此得到(或者简单的记为二进制最高位前补一个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) beginif (rst) begin// resetdata_out_G <= 0;endelse begindata_out_G <= data_out_G_n;endendendmodule



格雷码转二进制

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) beginif (rst) begin// resetdata_out_B <= 0;endelse begindata_out_B <= data_out_B_n;endendendmodule

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


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];endreg [width-1:0]data_out_G;always @(posedge clk or posedge rst) beginif (rst) begin// resetdata_out_G <= 0;endelse begindata_out_G <= data_out_G_n;endendendmodule


格雷码转二进制:

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) begindata_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];endreg [width-1:0]data_out_B;always @(posedge clk or posedge rst) beginif (rst) begin// resetdata_out_B <= 0;endelse begindata_out_B <= data_out_B_n;endendendmodule

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


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);    endendmodule

也就是说二进制转格雷码就是把二进制错位下然后按位亦或(果然如此),格雷码转二进制就是就是把当前位(包括当前的第k位)以前的所有格雷码按位亦或就好了,一观察果然是,厉害了。

我们再把转换那个图拿来看看,一看果然是对的。后面异步FIFO的设计就是用的这个代码。


附:

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

module work(clk,rst);input clk, rst;reg [7:0]data_in_B;always @(posedge clk or posedge rst) beginif (rst) begin// resetdata_in_B <= 0;endelse begindata_in_B <= data_in_B + 1;endendwire [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 beginrst = 1;#24 rst = 0;endwork u(.clk(clk),.rst(rst));endmodule


原创粉丝点击