0%

FPGA数字信号处理之编解码

本节主要对常见的编解码方式进行学习与记录。

格雷编码与解码

  • 格雷码的主要特点是相邻编码值中只有一个比特发生变化

  • 格雷编码被广泛应用于使用两个不同时钟的异步FIFO中。

  • 当位宽为多个比特的信号从一个时钟域传输到另外一个时钟域时,需要如图的转化电路

    image-20240529152839512
    • 该信号开始时转化为格雷码,然后进入源时钟域的寄存器
    • 此后,通过两级同步器同步到目的时钟域,实现同步后,通过相反的译码过程,就可以实现多比特值在两个时钟域之间的传递
  • 看了网上很多博客,我觉得只有这篇把在fifo中为什么要使用格雷码说清楚:异步FIFO:为什么要用格雷码_异步fifo为什么用格雷码-CSDN博客

    • 其中,有很重要一句话是:在fifo中格雷码的关键不在于发生亚稳态的概率小了,而在于即使发生了亚稳态,也没问题。
    • 我的理解是:因为相邻格雷码之间只有1bit发生变化,如果这一bit发生了亚稳态,稳定后,此bit要么是0,要么是1,即在fifo中要么是指针保持不变或者指针加1,但这不会影响fifo的结果,具体看这篇的解释(异步FIFO为什么要使用格雷码(笔记)_异步fifo使用格雷码的目的-CSDN博客
    • 还有一点就是,格雷码计数要变化的bit少,那么可能的中间状态就少。多一个中间状态,就相当于多一次变化,而每次变化都可能出现亚稳态

1.格雷码编码

  • binary_to_gray.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    module binary_to_gray #(
    parameter PTR = 8
    )(
    input [PTR : 0] binary_value,
    output [PTR : 0] gray_value
    );

    genvar i;
    generate
    for(i = 0; i < PTR; i = i + 1) begin
    assign gray_value[i] = binary_value[i] ^ binary_value[i + 1];
    end
    endgenerate

    assign gray_value[PTR] = binary_value[PTR];

    endmodule

2.格雷码解码

  • gray_to_binary.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    module gray_to_binary #(
    parameter PTR = 8
    )(
    input [PTR : 0] gray_value,
    output [PTR : 0] binary_value
    );

    assign binary_value[PTR] = gray_value[PTR];

    genvar i;
    generate
    for(i = 0; i < PTR; i = i + 1) begin
    assign binary_value[i] = binary_value[i + 1] ^ gray_value[i];
    end
    endgenerate

    endmodule

3.Testbench

  • transfer_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    module transfer_tb;

    parameter PTR = 3;

    reg clk ;
    reg [PTR : 0] binary_value ;
    wire [PTR : 0] gray_value ;
    wire [PTR : 0] binary_value_tf ;

    binary_to_gray #(
    .PTR(PTR)
    )binary_to_gray_u1(
    .binary_value (binary_value),
    .gray_value (gray_value )
    );

    gray_to_binary #(
    .PTR(PTR)
    )gray_to_binary_u1(
    .gray_value (gray_value),
    .binary_value (binary_value_tf)
    );

    initial begin
    clk = 1;
    end

    always #5 clk = ~clk;

    integer i;
    initial begin
    binary_value <= {(PTR + 1){1'b0}};
    for(i = 0; i < 10; i = i + 1) begin
    @(posedge clk) binary_value <= binary_value + 1;
    end
    end

    endmodule
    • 仿真结果:

      image-20240530010242387


优先级编码

  • 其实正常想法就是用if-else语句去写,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    module priority_encoder(
    input D0, D1, D2, D3, D4, D5, D6, D7,
    output reg [2 : 0] Q2Q1Q0
    );

    always @(*) begin
    Q2Q1Q0 = 3'b000;
    if(D0) Q2Q1Q0 = 3'b000;
    else if(D1) Q2Q1Q0 = 3'b001;
    else if(D2) Q2Q1Q0 = 3'b010;
    else if(D3) Q2Q1Q0 = 3'b011;
    else if(D4) Q2Q1Q0 = 3'b100;
    else if(D5) Q2Q1Q0 = 3'b101;
    else if(D6) Q2Q1Q0 = 3'b110;
    else if(D7) Q2Q1Q0 = 3'b111;
    end

    endmodule
    • 其RTL原理图如下:

      image-20240607013821245

  • 但在书上看到一种很神奇的case写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    module encoder(
    input D0, D1, D2, D3, D4, D5, D6, D7,
    output reg [2 : 0] Q2Q1Q0
    );

    always @(*) begin
    Q2Q1Q0 = 3'b000;
    case(1'b1)
    D0: Q2Q1Q0 = 3'b000;
    D1: Q2Q1Q0 = 3'b001;
    D2: Q2Q1Q0 = 3'b010;
    D3: Q2Q1Q0 = 3'b011;
    D4: Q2Q1Q0 = 3'b100;
    D5: Q2Q1Q0 = 3'b101;
    D6: Q2Q1Q0 = 3'b110;
    D7: Q2Q1Q0 = 3'b111;
    endcase
    end

    endmodule
    • 其RTL原理图如下:

      image-20240607013506799
    • 其实它相当于是对D0~D7依次扫描,优先级依次减小,哪个等于1,就执行其后对应的语句

  • 其实这两种写法综合出来的资源是一样的,都如下图所示

    image-20240607015927247

欢迎来到ssy的世界