关于格雷码的一点思考

关于格雷码的一点思考(图片修复)

什么是格雷码

在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。由于这种特点,就可以避免二进制编码计数组合电路中出现的亚稳态。格雷码常用于通信,FIFO 或者 RAM 地址寻址计数器中。

格雷码属于可靠性编码,是一种错误最小化的编码方式,因为虽然二进制码可以直接由数/模转换器转换成模拟信号,但在某些情况,例如从十进制的 7 转换为 8 时二进制码的每一位都要变,能使数字电路产生很大的尖峰电流脉冲。而格雷码则没有这一缺点,它在相邻数字间转换时,只有一位产生变化。它大大地减少了由一个状态到下一个状态时逻辑的混淆。

从广义上讲,格雷码(Gray Code)不止一种,在没做特殊说明的情况下,格雷码是指经典格雷码。下面是深度为4的二进制码和格雷码的对照表。

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

​ ​

二进制码与格雷码的转换及其推理过程

我们先将一个四位的二进制码的四个位分别设为A、B、C、D。

我们先从格雷码的最低位等于0的情况开始列写逻辑函数

image-20220302215603474 image-20220302220739062 image-20220302220835268 image-20220302221209159 image-20220302221304016 image-20220302221424681 image-20220302221507861 image-20220302221539226

通过比较第1、3、5、7张图片上的公式可以看出,A和B对于公式结果没有影响,所以可以省去。

这时候再结合 格雷码的最低位等于1的情况(用C、D位列写逻辑函数来表示,形式上类似于**(© * (D)')**),由于篇幅原因这里就不一一写出。

我们将格雷码的最低位设为F,那么通过C、D位的公式可以推导出:
( ( C ) ′ + ( D ) ′ ) ∗ ( ( C ) + ( D ) ) = F ((C)' + (D)')*((C) + (D)) = F ((C)+(D))((C)+(D))=F

( ( C ) ′ ∗ ( D ) ) + ( ( C ) ∗ ( D ) ′ ) = F ((C)' * (D))+((C) * (D)') = F ((C)(D))+((C)(D))=F
由上面两个式子可以得出
C ′ D + C D ′ = F C'D+CD' = F CD+CD=F

C ⨁ D = F C\bigoplus D = F CD=F

同理,可以得到二进制码转格雷码的方法:从最右侧开始,依次向左,将一位与其左边一位相异或,得到的数放入该位对应位置。

转码方式的核心思想

我们知道了转码方式,那为什么这样转码就可以产生相邻位转换仅变化一位的效果呢?

我们将6、7、8 三个的二进制数转化成波形图

image-20220303171933334

可以观察到,“6”有两处“电平变换”,“7”有一处,“8”有两处。对比6和7,变换数量发生了变化,对比“6”和“8”,变换的位置发生了变化。再对比更多的二进制数,可以发现,每一个二进制数的“电平变换”的分布是独有的,而且这种分布在相邻的数之间仅差一位。(这仅仅是我观察得到的结论,还没有严谨的证明,有证明或证伪方法的朋友可以提出来)这用我们就可以用上面异或的方式来表示“电平变换”的分布,就可以得到格雷码

其他转码方式

经典格雷码编码的核心思想就是通过某种运算来表示“电平变换”的分布,那还有什么方式可以产生同样的效果呢?

同或转换

就是将经典格雷码转码方式中的异或运算改为同或,这样可以得到经典格雷码的反码。

右侧异或

最右侧的最低位不变,从第二位开始,依次向左,位与右侧一位进行异或操作。当然,这样有一个问题,就是一个四位的二进制码很可能需要五位才可以表示,可能因此这种方法并不常用

image-20220303201755752

解码

知道了编码,解码也就非常简单了,只需要进行逆向运算就可以,我们以经典格雷码为例:

首先我们要找到格雷码的最高位,假设最高位是第四位

image-20220303205152832

最高位是第四位所隐含的条件是格雷码和二进制码的第五位都是0,所以
A ⨁ 0 = 1 A\bigoplus0 = 1 A0=1
易得 A = 1 A = 1 A=1

在来看下一位“E”

image-20220303210230480 $$ &1\bigoplus B&=E \\ (由异或的性质可得) \implies & 1\bigoplus E&=B $$

需要注意的是这里格雷码的“E”是已知量,所以我们可以求得“B”,再通过“B”和“F”求得“C”,以此类推。

image-20220303211358169

其他解码方式就不做介绍了,大家可以自己试着推导一下,相信你们也会有所收获

/************************************************************* 异步fifo 要求:解决跨时钟域跨数据位宽 设计者:王建森 时间:2025.10.28 *************************************************************/ module async_fifo ( parameter data_width = 4'd8 , //输入数据宽度 parameter data_depth = 4'd256 //数据深度 ) ( input wire wr_clk ,//写数据时钟 input wire wr_rst_n ,//写数据复位 input wire wr_en ,//写数据使能 input wire[data_width-1:0] wr_data ,//写入数据 input wire rd_clk ,//读数据时钟 input wire rd_rst_n ,//读数据复位 input wire rd_en ,//读数据使能 output wire[data_width-1:0] rd_data ,//读入数据 output wire full ,//写满标志 output wire empty //读空标志 ) //用二维数组实现RAM reg[data_width-1:0] fifo_buffer[data_depth-1:0] ; //读写地址指针 reg[$clog(data_depth):0] wr_ptr ;//写地址指针 reg[$clog(data_depth):0] wr_ptr_g1 ;//打两拍,避免亚稳态 reg[$clog(data_depth):0] wr_ptr_g2 ; reg[$clog(data_depth):0] rd_ptr ;//读地址指针 reg[$clog(data_depth):0] rd_ptr_g1 ;//打两拍,避免亚稳态 reg[$clog(data_depth):0] rd_ptr_g2 ; //读写地址指针 wire[data_depth-1:0] wr_addr ;//写地址 wire[data_depth-1:0] rd_addr ;//读地址 wire[$clog(data_depth):0] wr_ptr_g ;//写地址指针转格雷码 wire[$clog(data_depth):0] rd_ptr_g ;//读地址指针转格雷码 //写数据地址 assign wr_addr = wr_ptr[$clog(data_depth)-1:0] ; //读数据地址 assign rd_addr = rd_ptr[$clog(data_depth)-1:0] ; //写地址指令转为格雷码 assign wr_ptr_g = wr_ptr^(wr_ptr>>1) ; //读地址指令转为格雷码 assign rd_ptr_g = rd_ptr^(rd_ptr>>1) ; //写数据,更新写地址 always @ (posedge wr_clk or negedge wr_rst_n) begin if (~wr_rst_n) begin wr_ptr <= 0 ; end else if (wr_en && ~full) begin wr_ptr <= wr_ptr + 2'b1 ; fifo_buffer[wr_addr] <= wr_data ; end end //读数据,更新读地址 always @ (posedge rd_clk or negedge rd_rst_n) begin if (~rd_rst_n) begin rd_ptr <= 0 ; end else if (rd_en && ~empty ) begin rd_ptr <= rd_ptr + 1'b1 ; rd_data <= fifo_buffer[rd_addr] end end //打两拍,避免亚稳态 //将读指针的格雷码同步到写时钟域,来判断是否写满 always @ (posedge wr_clk or negedge wr_rst_n) begin if (~wr_rst_n) begin rd_ptr_g1 <= 0 ; rd_ptr_g2 <= 0 ; end else begin rd_ptr_g1 <= rd_ptr_g ; rd_ptr_g2 <= rd_ptr_g1 ; end end //将写指针的格雷码同步到读时钟域,来判断是否读空 always @ (posedge rd_clk or negedge rd_rst_n) begin if (~rd_rst_n) begin wr_ptr_g1 <= 0 ; wr_ptr_g2 <= 0 ; end else begin wr_ptr_g1 <= wr_ptr_g ; wr_ptr_g2 <= wr_ptr_g1 ; end end //写满 assign full = ( wr_ptr_g == { ~(rd_ptr_g2[$clog2(DATA_DEPTH) : $clog2(DATA_DEPTH) - 1]) ,rd_ptr_g2[$clog2(DATA_DEPTH) - 2 : 0]})? 1'b1 : 1'b0; //读空 assign empty = (wr_ptr_g2 == rd_ptr_g)? 1'b1 : 1'b0 ; endmodule找出这段代码的问题,并提出解决方法
最新发布
10-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

森林344

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

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

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

打赏作者

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

抵扣说明:

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

余额充值