4. JESD204C 8B/10B 解码器 FPGA 实现

任务说明

本案例在 FPGA 中实现一个 JESD204C 链路层常用的 8B/10B 解码器:把 10bit 编码字还原为 8bit 数据,同时输出 is_k(是否为 K 控制字符)、dec_err(码字非法或运行不一致)和 disp_err(当前极性下运行不一致)。为便于验证,顶层加入回环:用 8B/10B 编码器把测试字节编成 10bit,再送入解码器,比较解码结果与原始字节。

实现要点

  • 解码采用查表法:用 10bit 码字索引 1024 项 ROM,每项存 8bit 数据、is_k、is_valid、next_rd(解码后新的运行不一致极性)。
  • 运行不一致(running disparity)用 rd 状态跟踪:rd=0 表示负极性,rd=1 表示正极性。解码时若码字要求的极性不等于当前 rd,置 disp_err。
  • K 字符(K28.0~K28.7、K23.7、K27.7、K29.7、K30.7)单独识别,输出 is_k=1。
  • 回环验证:编码器对 0x00~0xFF 及若干 K 字符编码,解码后逐字节比对,统计错误数。
测试字节 8bit + is_k 8B/10B 编码 查表 + RD 8B/10B 解码 查表 + RD 比对 / 统计 err_cnt 10bit 码字 8bit + is_k + err 回环验证数据流

关键完整代码(Verilog)

// 8B/10B 解码器 + 回环验证
// 码表:1024 项,每项 {is_valid, is_k, next_rd, data[7:0]}
// rd: 0=负极性, 1=正极性
module jesd204c_8b10b_decode (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [9:0]  code_in,
    input  wire        rd_in,
    output reg  [7:0]  data_out,
    output reg         is_k,
    output reg         dec_err,
    output reg         disp_err,
    output reg         rd_out
);
    reg [10:0] rom [0:1023]; // {is_valid, is_k, next_rd, data[7:0]}

    integer i;
    initial begin
        for (i = 0; i < 1024; i = i + 1) rom[i] = 11'd0;
        // D 字符:仅列出常用示例,实际工程用脚本生成全表
        rom[10'b1001110100] = {1'b1,1'b0,1'b1,8'h00}; // D0.0 RD-
        rom[10'b0110001011] = {1'b1,1'b0,1'b0,8'h00}; // D0.0 RD+
        rom[10'b1010110001] = {1'b1,1'b0,1'b1,8'hFF}; // D31.7 RD-
        rom[10'b0101001110] = {1'b1,1'b0,1'b0,8'hFF}; // D31.7 RD+
        // K 字符示例
        rom[10'b0011111010] = {1'b1,1'b1,1'b1,8'hBC}; // K28.5 RD-
        rom[10'b1100000101] = {1'b1,1'b1,1'b0,8'hBC}; // K28.5 RD+
        rom[10'b1110101000] = {1'b1,1'b1,1'b1,8'hF7}; // K23.7 RD-
        rom[10'b0001010111] = {1'b1,1'b1,1'b0,8'hF7}; // K23.7 RD+
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 8'h00; is_k <= 1'b0;
            dec_err <= 1'b0; disp_err <= 1'b0; rd_out <= 1'b0;
        end else begin
            if (rom[code_in][10]) begin
                data_out <= rom[code_in][7:0];
                is_k     <= rom[code_in][9];
                dec_err  <= 1'b0;
                disp_err <= (rom[code_in][8] != rd_in);
                rd_out   <= rom[code_in][8];
            end else begin
                data_out <= 8'h00; is_k <= 1'b0;
                dec_err  <= 1'b1; disp_err <= 1'b0;
                rd_out   <= rd_in;
            end
        end
    end
endmodule

// 回环验证顶层:编码 -> 解码 -> 比对
module tb_loopback;
    reg clk = 0, rst_n = 0;
    reg [7:0] tx_data; reg tx_is_k;
    wire [9:0] enc_code; wire enc_rd;
    wire [7:0] rx_data; wire rx_is_k, dec_err, disp_err, rx_rd;
    integer err_cnt = 0; integer i;

    // 简化编码器:仅对测试用码字做映射(与解码表对应)
    reg [9:0] enc_rom [0:255];
    initial begin
        enc_rom[8'h00] = 10'b1001110100;
        enc_rom[8'hFF] = 10'b1010110001;
        enc_rom[8'hBC] = 10'b0011111010; // K28.5
        enc_rom[8'hF7] = 10'b1110101000; // K23.7
    end
    assign enc_code = enc_rom[tx_data];
    assign enc_rd   = 1'b0;

    jesd204c_8b10b_decode u_dec (
        .clk(clk), .rst_n(rst_n), .code_in(enc_code), .rd_in(enc_rd),
        .data_out(rx_data), .is_k(rx_is_k),
        .dec_err(dec_err), .disp_err(disp_err), .rd_out(rx_rd)
    );

    always #5 clk = ~clk;

    initial begin
        #20 rst_n = 1;
        // 遍历测试向量
        for (i = 0; i < 4; i = i + 1) begin
            case (i)
                0: begin tx_data = 8'h00; tx_is_k = 0; end
                1: begin tx_data = 8'hFF; tx_is_k = 0; end
                2: begin tx_data = 8'hBC; tx_is_k = 1; end
                3: begin tx_data = 8'hF7; tx_is_k = 1; end
            endcase
            @(posedge clk); #1;
            if (rx_data !== tx_data || rx_is_k !== tx_is_k || dec_err)
                err_cnt = err_cnt + 1;
        end
        $display("loopback done, err_cnt=%0d", err_cnt);
        if (err_cnt == 0) $display("PASS");
        else              $display("FAIL");
        $finish;
    end
endmodule

运行与验证

  1. 用 Icarus Verilog 仿真:iverilog -o sim tb_loopback.v jesd204c_8b10b_decode.v && vvp sim。
  2. 观察输出:loopback done, err_cnt=0 且打印 PASS 即回环验证通过。
  3. 综合上板时,把 jesd204c_8b10b_decode 接到 JESD204C 接收链路,code_in 来自 SerDes 解串输出,rd_in 由上一级 rd_out 级联。
  4. 错误检测验证:向 code_in 送入未在 ROM 中定义的码字,应观察到 dec_err=1;送入与当前 rd_in 极性不符的合法码字,应观察到 disp_err=1。

说明

示例 ROM 只列出少量码字用于演示解码与回环流程。实际工程中应使用脚本按 8B/10B 标准生成全部 256 个 D 字符和 12 个 K 字符的正负极性码字,填入 1024 项 ROM,即可覆盖完整 JESD204C 链路层解码需求。