二进制格雷码与二进制自然码


---- # 二进制自然码和二进制格雷码转换以及格雷码计数器 ----

格雷码优点

  格雷码的优点都来源于其固有特性:相邻两个数值之间只有1bit跳变:

  1. 抗干扰 ;
  2. 低功耗 ;



二进制自然码 -> 二进制格雷码

二进制转格雷码

module bin2gry(Gry,Bin);
    parameter length = 8;     //以八位示例
    output [length-1:0] Gry;
    input  [length-1:0] Bin;
    reg    [length-1:0] Gry;
    integer i;

    always @ (Bin) begin
        for(i=0; i<length-1; i=i+1)
            Gry[i] = Bin[i] ^ Bin[i+1];
        Gry[i] = Bin[i];
    end
/*另一种简单的实现方法如下:*/
// assign Gray = (Bin >> 1) ^ Bin;
endmodule

Python 获取格雷码

N = 4
binnum =  [i for i in range(2**N)]
decnum =  [i^(i>>1) for i in range(2**N)]
#
def get_bin (din, N):
    relist = []
    for n in din:
        src_bin = bin(n).lstrip('0b')
        len_bin = len(src_bin)
        if(len_bin < N):
            relist.append((N-len_bin)*'0' + src_bin)
        else:
            relist.append(src_bin)
    return relist
#gray = []
#dbin = []
gray = get_bin(decnum, N)
dbin = get_bin(binnum, N)
#print(dbin)
#print(binnum)
#len_n = len(gray)
print ('-- gray | bin | dec --')
for i in range(0, 2**N):
    print( gray[i] + ' // ' + dbin[i] + ' // ' + str(i))
#print(gray)



二进制格雷码 -> 二进制自然码

二进制转格雷码

module gry2bin(Gry,Bin);
    parameter length = 8;
    input  [length-1:0] Gry;
    output [length-1:0] Bin;
    reg    [length-1:0] Bin;
    integer i;

    always @ (Gry) begin       
        Bin[length-1] = Gry[length-1];       
        for(i=length-2; i>=0; i=i-1)               
            Bin[i] = Bin[i+1] ^ Gry[i];
    end
endmodule



可综合的格雷码计数器:

1. 通过二进制转换

  下面这个例子是通过将格雷码转换为二进制,二进制再输入加法器,加法器结果再转换为格雷码。因为两次转换的组合逻辑的关系,所以如果综合器优化的不够好的话会限制计数器的速度。
  当然,也可以像注释部分那样,先做一个二进制计数器再进行二进制转格雷码(滞后一个周期)。但是多消耗了1倍的寄存器数量,这在FPGA上这是可以接受的,在ASIC上就要考虑面积问题值不值得了。
  如果计数器比较小,可以直接用格雷码状态机当格雷码计数器,你甚至可以画卡诺图搭一个电路。
quartus ii 13.1综合的格雷码计数器

`timescale 1ns/1ps
module gray_bin_cnt #(
    parameter GRAYWIDTH = 4 ,
	 parameter DLY = 0.5 
)(
    input           clk        ,
	 input           rst_n      ,
	 input           cnt_en     ,
	 //input           cnt_rst    ,
	 output wire  [GRAYWIDTH-1 : 0]    gray_dout  
) ;
   
    reg  [GRAYWIDTH-1 : 0] gray_cnt ;
	 reg  [GRAYWIDTH-1 : 0] bin_cnt  ;
	 //wire [GRAYWIDTH-1 : 0] bin_cnt  ;
	 
	 // 十进制计数器
	 /*
    always @(posedge clk or negedge rst_n)   // 寄存器类型二进制计数器
	     if (rst_n == 1'b0)
		      bin_cnt <= #DLY{GRAYWIDTH-1{1'b0}} ;
			else 
			   bin_cnt <= #DLY bin_cnt + {{GRAYWIDTH-1{1'b0}}, 1'b1} ;
	 */
	 //assign bin_cnt = gray_bin(1'b0, gray_cnt) + {{GRAYWIDTH-1{1'b0}}, 1'b1} ;
	 always @(*) begin                        // 组合逻辑(码制转换+加法器)
	     bin_cnt = {GRAYWIDTH{1'bx}} ; //@ loopback
		  if (cnt_en)
		      bin_cnt = {GRAYWIDTH{1'b0}} ;
		  else if (bin_cnt > {GRAYWIDTH{1'b1}} - {{GRAYWIDTH-1{1'b0}}, 1'b1}) //计数到 2^n -2
		      bin_cnt = {GRAYWIDTH{1'b0}} ;
		  else
		      bin_cnt = gray_bin(1'b0, gray_cnt) + {{GRAYWIDTH-1{1'b0}}, 1'b1} ; //加法器,格雷码转换二进制
	 end
				
	 // 格雷码计数器
	 always @(posedge clk or negedge rst_n)
	     if (rst_n == 1'b0)
		      gray_cnt <= #DLY 'd0 ;
			else 
			   gray_cnt <= #DLY gray_bin(1'b1, bin_cnt) ;		// 二进制转换格雷码
				
	assign gray_dout = gray_cnt ;

//function automatic [GRAYWIDTH-1:0] gray_bin (input option, input [GRAYWIDTH-1:0] din) ;
function automatic [GRAYWIDTH-1:0] gray_bin ;
    input                   option ;
    input   [GRAYWIDTH-1:0] din    ;
    integer                 i      ;
    begin
        if (option == 1'b1)              //编码, 二进制->格雷码
            gray_bin = (din >> 1) ^ din ;
        else if (option == 1'b0) begin   //解码, 格雷码->二进制
            gray_bin[GRAYWIDTH-1] = din [GRAYWIDTH-1] ;
            for (i = GRAYWIDTH-2; i >= 0; i = i-1)
                gray_bin[i] = gray_bin[i+1] ^ din[i] ;
        end
    end
endfunction 

endmodule



2. 通过状态机构造

  状态机格雷码,注意在FPGA综合时取消优化,否则优化成了独热码,综合出来和实际编写不符。当计数器位数比较多的时候写起来比较繁琐。
格雷码状态机作为计数器

`timescale 1ns/1ps
module gray_bin_cnt #(
    parameter GRAYWIDTH = 4 ,
    parameter DLY = 0.5 
)(
    input           clk        ,
    input           rst_n      ,
    input           cnt_en     ,
     //input           cnt_rst    ,
    output wire  [GRAYWIDTH-1 : 0]    gray_dout  
) ;
    localparam [GRAYWIDTH-1 : 0] 
                //--   gray    | bin  | dec --
               S0   = 4'b0000, // 0000 // 0
               S1   = 4'b0001, // 0001 // 1
               S2   = 4'b0011, // 0010 // 2
               S3   = 4'b0010, // 0011 // 3
               S4   = 4'b0110, // 0100 // 4
               S5   = 4'b0111, // 0101 // 5
               S6   = 4'b0101, // 0110 // 6
               S7   = 4'b0100, // 0111 // 7
               S8   = 4'b1100, // 1000 // 8
               S9   = 4'b1101, // 1001 // 9
               S10  = 4'b1111, // 1010 // 10
               S11  = 4'b1110, // 1011 // 11
               S12  = 4'b1010, // 1100 // 12
               S13  = 4'b1011, // 1101 // 13
               S14  = 4'b1001, // 1110 // 14
               S15  = 4'b1000; // 1111 // 15

    reg [GRAYWIDTH-1 : 0]  state, next ;

    always @(posedge clk or negedge rst_n)
        if (rst_n == 1'b0)  state <= #DLY S0 ;
        else if(cnt_en)     state <= #DLY next ;    
    
    always @(*) begin
        next = 'bx ;
        case (state)
            S0  : next = S1   ;
            S1  : next = S2   ;
            S2  : next = S3   ;
            S3  : next = S4   ;
            S4  : next = S5   ;
            S5  : next = S6   ;
            S6  : next = S7   ;
            S7  : next = S8   ;
            S8  : next = S9   ;
            S9  : next = S10  ;
            S10 : next = S11  ;
            S11 : next = S12  ;
            S12 : next = S13  ;
            S13 : next = S14  ;
            S14 : next = S0   ;
            S15 : next = S0   ;
            default : next = S0 ;
        endcase
    end

    assign gray_dout = state ;



3. 通过优化逻辑生成

参考:

(1) Quartus II, Edit -> Insert Template -> Verilog HDL -> Full Designs -> Arithmetic -> Counters -> Gray Counter

(2) 《第六届全国核仪器及其应用学术会议论文集 - 格雷码计数器的优化设计_陈晓磊,范如玉,李斌康》

  二进制计数器的组合逻辑部分主要由加法器构成,一般情况下加法器是级联结构的(行波进位加法器)。基于同样的思路,格雷码应当也能有某种级联的组合逻辑构成。
  如下表我们仔细观察格雷码,可以看出:
   (1) 计数器最低位的翻转频率是计数时钟频率的 1/4
   (2) 当且仅当第 N 位为“1”,N-1 及以后的位都为“0”时,下一个时钟到来后第 N+1 位发生翻转

  基于以上两点,可以通过一个D触发器将时钟二分频,通过这个触发器和格雷码本身的触发器来组合出逻辑控制循环计数。假设格雷码计数器位数是5bit:gray [4:0] ,把时钟二分频作为辅助的最低位:gray[-1]。
  可以得到如下等式:
  注:gray[-1]异步置位,随时钟翻转,计数器最高位为了保证循环和次高位做了或运算逻辑。

  1. gray[0] = gray[0] ^ ( gray[-1] & 1'b1 )
  2. gray[1] = gray[1] ^ ( gray[0] & ~gray[-1] )
  3. gray[2] = gray[2] ^ ( gray[1] & ~gray[-1] & ~gray[0] )
  4. gray[3] = gray[3] ^ ( gray[2] & ~gray[-1] & ~gray[0] & ~gray[1] )
  5. gray[4] = gray[4] ^ ( (gray[3] | gray[4]) & ~gray[-1] & gray[0] & gray[1] & gray[2] )
格雷码 二进制 十进制 格雷码+时钟二分频
0 0 0 0 0000 0 0 0 0 0 - 1
0 0 0 1 0001 1 0 0 0 1 - 0
0 0 1 1 0010 2 0 0 1 1 - 1
0 0 1 0 0011 3 0 0 1 0 - 0
0 1 1 0 0100 4 0 1 1 0 - 1
0 1 1 1 0101 5 0 1 1 1 - 0
0 1 0 1 0110 6 0 1 0 1 - 1
0 1 0 0 0111 7 0 1 0 0 - 0
1 1 0 0 1000 8 1 1 0 0 - 1
1 1 0 1 1001 9 1 1 0 1 - 0
1 1 1 1 1010 10 1 1 1 1 - 1
1 1 1 0 1011 11 1 1 1 0 - 0
1 0 1 0 1100 12 1 0 1 0 - 1
1 0 1 1 1101 13 1 0 1 1 - 0
1 0 0 1 1110 14 1 0 0 1 - 1
1 0 0 0 1111 15 1 0 0 0 - 0
5bit 逻辑计算的格雷码
`timescale 1ns/1ps
module gray_bin_cnt #(
    parameter GRAYWIDTH = 5 ,
    parameter DLY = 0.5 
)(
    input           clk        ,
    input           rst_n      ,
    input           cnt_en     ,
     //input           cnt_rst    ,
    output wire  [GRAYWIDTH-1 : 0]    gray_dout  
) ;

      reg  [GRAYWIDTH-1:-1] gray ;
      reg  [GRAYWIDTH-2:-1] one_below ;
      wire  msb_flag ;
      integer i, j ;
 
    //部分级联的组合逻辑
     always @(*) begin
         one_below[-1] = 1'b1 ;
          for (j = 0; j <= GRAYWIDTH-2; j = j + 1)
              one_below[j] = one_below[j-1] & ~gray[j-1] ;
     end 
     
     assign msb_flag = gray[GRAYWIDTH-1] | gray[GRAYWIDTH-2];
     
     always @(posedge clk or negedge rst_n)
         if (rst_n == 1'b0)
              gray <= {{GRAYWIDTH{1'b0}}, 1'b1} ; 
          else if (cnt_en) begin
              gray[-1] <= ~gray[-1] ;
              for (i = 0; i < GRAYWIDTH-1; i = i + 1)
                    gray[i] <= gray[i] ^ (gray[i-1] & one_below[i-1]) ;
                gray[GRAYWIDTH-1] <= gray[GRAYWIDTH-1] ^ (msb_flag & one_below[GRAYWIDTH-2] )  ;
          end 
                
    assign gray_dout = gray[GRAYWIDTH-1:0] ;
     

endmodule

4. 总结

  一般为了简单推荐二进制转换的方式,不易出错。
  尽管优化逻辑生成的格雷码计数器相对省资源和逻辑简单,但是当位数太大就不推荐此方式了,二进制转换的方式加法器部分可以用超前进位方式换取速度(感觉拆分行波进位加法器也可以)。




CDC书籍:
https://github.com/yllinux/blogPic/blob/master/doc/CummingsSNUG2008Boston_CDC.pdf

参考:https://blog.csdn.net/gordon_77/article/details/79489548
https://www.cnblogs.com/ZcsTech/p/3500207.html
https://zhuanlan.zhihu.com/p/130732799

posted @ 2020-06-08 22:40  纟彖氵戋  阅读(780)  评论(0编辑  收藏  举报