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 字符编码,解码后逐字节比对,统计错误数。
关键完整代码(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
运行与验证
- 用 Icarus Verilog 仿真:
iverilog -o sim tb_loopback.v jesd204c_8b10b_decode.v && vvp sim。 - 观察输出:
loopback done, err_cnt=0且打印PASS即回环验证通过。 - 综合上板时,把
jesd204c_8b10b_decode接到 JESD204C 接收链路,code_in来自 SerDes 解串输出,rd_in由上一级rd_out级联。 - 错误检测验证:向
code_in送入未在 ROM 中定义的码字,应观察到dec_err=1;送入与当前rd_in极性不符的合法码字,应观察到disp_err=1。
说明
示例 ROM 只列出少量码字用于演示解码与回环流程。实际工程中应使用脚本按 8B/10B 标准生成全部 256 个 D 字符和 12 个 K 字符的正负极性码字,填入 1024 项 ROM,即可覆盖完整 JESD204C 链路层解码需求。