基于FPGA查找表的8b10b编码


参考文献

[1] https://blog.csdn.net/qq_37654178/article/details/112391245

[2] https://ieeexplore.ieee.org/document/5390392
//8b10b编码原论文

前言

  在高速串行通信中,信号可能会受到噪声、衰减和干扰等影响,在实际应用中可以使用8b10b等编码技术增加信号的抗干扰能力,提高了数据传输的可靠性,使得高速串行通信更加稳定和可靠。8b/10b方式最初由IBM公司于1983年发明并应用于ESCON (200M互连系统),由Al Widmer和Peter Franaszek在IBM的刊物“研究与开发”上描述。本文基于查找表实现8b10b编码。

一、8b10b编码

  顾名思义,8b10b编码是在高速串行通信中将8bit的数据映射成10bit的码字进行传输。直观看来,8b10b编码带来了20%的数据冗余,那舍弃效率带来的好处是什么呢?

  8b10b编码最直接的好处是实现了直流均衡。在高速串行传输中,通常采用交流耦合方式,发送和接收端之间串联电容,利用电容“通交隔直”的特性去除信号的直流偏置,使得接收和发送端处于同一电平标准。但万物有利弊,当串行数据流出现多个连续的0或1时,可以认为该时间段信号是直流的,直流信号被滤除,幅度降低,导致接收端无法正确识别0和1,使得解码失败。

  这里回顾下电容“通交隔直”的原理。根据理想电容阻抗公式
Z c = 1 2 π f c Z_c = \frac{1}{2{\pi}fc} Zc=2πfc1
  信号频率越高,电容阻抗越低,当信号为直流时,f=0,此时电容阻抗趋近于无穷,信号无法通过。

  那8b10b编码又是如何实现这个直流均衡的呢?核心思路便是不让串行数据出现连续的5个0或1。首先我们先了解下8b10b编码的组成,8b10b编码包含256数据码和12控制码,8bit原始数据对应256个数据码加上12个控制码,而编码后的10bit数据有1024个码,因此8b10b实际上就是从1024个码中挑选合适的码来表示数据码和控制码,使得编码后的码字0和1的数量尽可能相等且不出现过长的0或1。

二、基本原理

  假设原始8bit数据由高到低用HGFEDCBA表示,8b10b编码先将8bit数据分成高3位HGF和低5位EDCBA两部分,然后经过3b4b编码将高3位映射到fghj;同时低5位映射成abcdei。最后合并成abcdeifghj发送,发送为小端发送,低位a先发送。
请添加图片描述

  由上文可知,8b10b编码可以分成控制码D.x.y和数据码K.x.y,其中x表示为低五位的十进制数值,y表示为高三位的十进制数值,例如D.1.0表示的时8bit数000_00001代表的原始数据码。顾名思义,数据码是对数据进行编码,而控制码是用来标识传输的开始、结束等状态,控制码也称为K码或者是Comma码,用于高速传输接收端的数据对齐和恢复。比如8b10b编码的协议通常使用K28.5(正码10’b1110000011,负码10’b0001111100)来作为对齐码。

以下表1是对低5位进行数据编码
请添加图片描述

以下表2是对高3位进行数据编码
请添加图片描述

以下表3是8b10b的部分编码
请添加图片描述

以下表4是12种控制码的编码
请添加图片描述

  由上面三个编码表,我们可以发现同个8位的数据有可能会有两种不同的编码方式,下面将介绍如何判断应该选择哪种编码。首先啊我们先了解“disparity”“running disparity(RD)”的概念。

  disparity为不一致性或者极性,表示编码后的数据中“0”和“1”的个数差值。由上表可以的得到编码后的数据极性只存在三种情况,分别为“+2”(1比0多两个),“0”(0和1一样多),“-2”(1比0少两个)。我们称极性为0的编码称为平衡码。

  RD为运行不一致性或者极性偏差,RD是数据流的极性特性,是当前时刻之前所有数据极性的统计,如果之前数据流中1比0多,则RD为正,否则为负。8b10b编码便是根据串行数据流的RD特性进行控制编码,改善编码的直流特性。举例说明,见表1,比如说当前时刻要传输数据的低5位为D.00,如果之前数据流1比0多RD为正时,此时编码应该增加0的个数,D.00应该编码为6’b011000,反之编码为6’b100111。下图为RD状态转移图。
请添加图片描述

  如下表Rules for running disparity,也就是RD的选择原则。由该表格可以得到三种规律。一是当编码的极性Disparity为0时,previous RD和next RD保持不变。二是当编码的极性Disparity可为 ± 2 \pm2 ±2且previous RD为-1时,编码极性选择+2,next RD与previous RD取反。三是当编码的极性Disparity可为 ± 2 \pm2 ±2且previous RD为+1时,编码极性选择-2,next RD与previous RD取反。

请添加图片描述

  对8b10b编码举例说明,当当前数据为D.0.00,先发送低5位,再发送高3位。初始化RD为负,对于低5位来说,previous RD = -1(0多),则低5位5’b00000编码6’b100111(disparity = +2,1多),此时next RD = +1(1多)。后续发送高3位,对于高3位来说,previous RD = +1,则高3位3’b000编码为4’b0100(disparity = -2,0多),此时next RD = -1(0多)。对比表3可以知道,此处推导D.0.00(RD-)的编码为10’b100111_0100,推导成功。

三、实现

8b10b编码

module encode_8bto10b(
    input clk,
    input rst_n,
    input din_en,            //输入使能
    input is_k,              //是否为k码
    input [7:0] din_8b,      //输入的8bit数据
    output reg [9:0] dout_10b    //输出的10bit数据
);
reg RD_pre,RD_next;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)  {RD_next,dout_10b} <= 'd0;
    else if(din_en)        {RD_next,dout_10b} <= code_10b(is_k,RD_pre,din_8b);
    else {RD_next,dout_10b} <= {RD_next,dout_10b};
end
    
always @(posedge clk) RD_pre <= RD_next;

function [10:0] code_10b;
    input is_k;
    input RD_pre;       //上一次的RD
    input [7:0] din_8b;
    //中间寄存器
    reg RD_mid;     //经过6b编码后的RD
    reg is_k28;
    reg [5:0] b6;
    reg [3:0] b4;
    reg is_P7,A7_cnd1,A7_cnd2;
    integer i;      //循环变量
    
    //输出
    reg [9:0] dout_10b;
    reg RD_next;     //经过4b编码后的RD
    
    begin
    
    //判断是否为控制码
    is_k28 = is_k && (din_8b[4:0] == 5'd28);
    //判断是用P7编码还是A7编码,数据码一般使用P7编码,只有在下列情况才用A7编码,目的是防止出现5个连0或者连1
    //if RD=-1, 5b = 17,18,20
    //if RD=+1, 5b = 11,13,14
    A7_cnd1 = (!RD_pre) & (din_8b[7:3] == 5'b11110 & (din_8b[2:0] == 3'b001 | din_8b[2:0] == 3'b010 | din_8b[2:0] == 3'b100));
    A7_cnd2 = (RD_pre) & (din_8b[7:3] == 5'b11101 & (din_8b[2:0] == 3'b011 | din_8b[2:0] == 3'b101 | din_8b[2:0] == 3'b110));
    
    is_P7 = !(is_k | A7_cnd1 | A7_cnd2);
    
    //5b to 6b code
    if(is_k28) begin
            b6 = (RD_pre)?6'b110000:6'b001111;  //abcdei
            RD_mid = ~RD_pre;
        end
    else 
        case(din_8b[4:0])       //input EDCBA  output abcdei
            5'd0 :begin b6 = (RD_pre)? 6'b011000:6'b100111; RD_mid = ~RD_pre;  end      //RD_mid是否反向的判断依据是,编码后有是否为完美编码(01平衡)
            5'd1 :begin b6 = (RD_pre)? 6'b100010:6'b011101; RD_mid = ~RD_pre;  end
            5'd2 :begin b6 = (RD_pre)? 6'b010010:6'b101101; RD_mid = ~RD_pre;  end
            5'd3 :begin b6 = 6'b110001; RD_mid = RD_pre;  end
            5'd4 :begin b6 = (RD_pre)? 6'b001010:6'b110101; RD_mid = ~RD_pre;  end
            5'd5 :begin b6 = 6'b101001; RD_mid = RD_pre;  end
            5'd6 :begin b6 = 6'b011001; RD_mid = RD_pre;  end
            5'd7 :begin b6 = (RD_pre)? 6'b000111:6'b111000; RD_mid = RD_pre;  end
            5'd8 :begin b6 = (RD_pre)? 6'b000110:6'b111001; RD_mid = ~RD_pre;  end
            5'd9 :begin b6 = 6'b100101; RD_mid = RD_pre;  end
            5'd10:begin b6 = 6'b010101; RD_mid = RD_pre;  end
            5'd11:begin b6 = 6'b110100; RD_mid = RD_pre;  end
            5'd12:begin b6 = 6'b001101; RD_mid = RD_pre;  end
            5'd13:begin b6 = 6'b101100; RD_mid = RD_pre;  end
            5'd14:begin b6 = 6'b011100; RD_mid = RD_pre;  end
            5'd15:begin b6 = (RD_pre)? 6'b101000:6'b010111; RD_mid = ~RD_pre;  end
            5'd16:begin b6 = (RD_pre)? 6'b100100:6'b011011; RD_mid = ~RD_pre;  end
            5'd17:begin b6 = 6'b100011; RD_mid = RD_pre;  end
            5'd18:begin b6 = 6'b010011; RD_mid = RD_pre;  end
            5'd19:begin b6 = 6'b110010; RD_mid = RD_pre;  end
            5'd20:begin b6 = 6'b001011; RD_mid = RD_pre;  end
            5'd21:begin b6 = 6'b101010; RD_mid = RD_pre;  end
            5'd22:begin b6 = 6'b011010; RD_mid = RD_pre;  end
            5'd23:begin b6 = (RD_pre)? 6'b000101:6'b111010; RD_mid = ~RD_pre;  end
            5'd24:begin b6 = (RD_pre)? 6'b001100:6'b110011; RD_mid = ~RD_pre;  end
            5'd25:begin b6 = 6'b100110; RD_mid = RD_pre;  end
            5'd26:begin b6 = 6'b010110; RD_mid = RD_pre;  end
            5'd27:begin b6 = (RD_pre)? 6'b001001:6'b110110; RD_mid =~RD_pre;  end
            5'd28:begin b6 = 6'b001110; RD_mid = RD_pre; end
            5'd29:begin b6 = (RD_pre)? 6'b010001:6'b101110; RD_mid = ~RD_pre;  end
            5'd30:begin b6 = (RD_pre)? 6'b100001:6'b011110; RD_mid = ~RD_pre;  end
            5'd31:begin b6 = (RD_pre)? 6'b010100:6'b101011; RD_mid = ~RD_pre;  end
            default:begin b6 = 6'bXXXXXX;RD_mid = RD_pre;  end
        endcase
    //3b to 4b code
    case(din_8b[7:5])       //input HGF  output fghj
        3'd0:begin b4 = (RD_mid)?           4'b0100:4'b1011; RD_next = ~RD_mid; end
        3'd1:begin b4 = (!RD_mid & is_k28)? 4'b0110:4'b1001; RD_next = RD_mid; end
        3'd2:begin b4 = (!RD_mid & is_k28)? 4'b1010:4'b0101; RD_next = RD_mid; end
        3'd3:begin b4 = (RD_mid)?           4'b0011:4'b1100; RD_next = RD_mid; end
        3'd4:begin b4 = (RD_mid)?           4'b0010:4'b1101; RD_next = ~RD_mid; end
        3'd5:begin b4 = (!RD_mid & is_k28)? 4'b0101:4'b1010; RD_next = RD_mid; end
        3'd6:begin b4 = (!RD_mid & is_k28)? 4'b1001:4'b0110; RD_next = RD_mid; end
        3'd7:begin 
                RD_next = ~RD_mid;
                if(is_P7)  b4 = (RD_mid)?4'b0001:4'b1110;
                else       b4 = (RD_mid)?4'b0111:4'b1000;
             end
        default:begin b4 = 4'bXXXX;  RD_next = RD_mid; end
    
    endcase
    
    code_10b = {RD_next,{b6,b4}};
    end
    
endfunction
endmodule

8b10b解码


module decode_10bto8b(
    input clk,
    input rst_n,
    input [9:0] din_10b,
    output [7:0] dout_8b
    );
reg [2:0] dout_3b_r;
reg [4:0] dout_5b_r;
reg is_k28 ;
wire [3:0] din_4b;
wire [5:0] din_6b;
assign din_4b = din_10b[3:0];
assign din_6b = din_10b[9:4];

initial is_k28 = 0;
//4b to 3b
//always @(posedge clk or negedge rst_n)begin
//        if(!rst_n)
//            dout_3b_r <= 'd0;
//        else 
always @(*)begin    //6和1 2和5是编码重复但反向的
            case(din_4b)
            4'b1011:dout_3b_r =  3'd0;
            4'b0100:dout_3b_r =  3'd0;
            4'b1001:dout_3b_r = (is_k28)? 3'd6: 3'd1;       //(D28)?
            4'b0110:dout_3b_r = (is_k28)? 3'd1: 3'd6;      //K码的编码,RD=-1
//            4'b0110:dout_3b_r =  3'd6;
//            4'b1001:dout_3b_r =  3'd6;      //K码的编码,RD=-1
            
            4'b0101:dout_3b_r = (is_k28)? 3'd5: 3'd2;      //(D28)?
            4'b1010:dout_3b_r = (is_k28)? 3'd2: 3'd5;      //K码的编码,RD=-1
//            4'b1010:dout_3b_r =  3'd5;
//            4'b0101:dout_3b_r =  3'd5;      //K码的编码,RD=-1
            4'b1100:dout_3b_r =  3'd3;
            4'b0011:dout_3b_r =  3'd3;
            4'b1101:dout_3b_r =  3'd4;
            4'b0010:dout_3b_r =  3'd4;


            4'b1110:dout_3b_r =  3'd7;//P7
            4'b0001:dout_3b_r =  3'd7;//P7
            4'b0111:dout_3b_r =  3'd7;//A7,K
            4'b1000:dout_3b_r =  3'd7;//A7,K
            default: dout_3b_r = 3'bXXX; 
            endcase
    end
    
//6b to 5b
//    always @(posedge clk or negedge rst_n)begin
//        if(!rst_n)
//            dout_5b_r <= 'd0;
//        else 
always @(*)begin
            case(din_6b)
               6'b100111 : dout_5b_r = 5'd0 ;
               6'b011101 : dout_5b_r = 5'd1 ;
               6'b101101 : dout_5b_r = 5'd2 ;
               6'b110001 : dout_5b_r = 5'd3 ;
               6'b110101 : dout_5b_r = 5'd4 ;
               6'b101001 : dout_5b_r = 5'd5 ;
               6'b011001 : dout_5b_r = 5'd6 ;
               6'b111000 : dout_5b_r = 5'd7 ;
               6'b111001 : dout_5b_r = 5'd8 ;
               6'b100101 : dout_5b_r = 5'd9 ;
               6'b010101 : dout_5b_r = 5'd10;
               6'b110100 : dout_5b_r = 5'd11;
               6'b001101 : dout_5b_r = 5'd12;
               6'b101100 : dout_5b_r = 5'd13;
               6'b011100 : dout_5b_r = 5'd14;
               6'b010111 : dout_5b_r = 5'd15;
               6'b011011 : dout_5b_r = 5'd16;
               6'b100011 : dout_5b_r = 5'd17;
               6'b010011 : dout_5b_r = 5'd18;
               6'b110010 : dout_5b_r = 5'd19;
               6'b001011 : dout_5b_r = 5'd20;
               6'b101010 : dout_5b_r = 5'd21;
               6'b011010 : dout_5b_r = 5'd22;
               6'b111010 : dout_5b_r = 5'd23;
               6'b110011 : dout_5b_r = 5'd24;
               6'b100110 : dout_5b_r = 5'd25;
               6'b010110 : dout_5b_r = 5'd26;
               6'b110110 : dout_5b_r = 5'd27;
               6'b001110 : dout_5b_r = 5'd28;
               6'b101110 : dout_5b_r = 5'd29;
               6'b011110 : dout_5b_r = 5'd30;
               6'b101011 : dout_5b_r = 5'd31;
               6'b011000 : dout_5b_r = 5'd0 ;
               6'b100010 : dout_5b_r = 5'd1 ;
               6'b010010 : dout_5b_r = 5'd2 ; 
               6'b001010 : dout_5b_r = 5'd4 ;
               6'b000111 : dout_5b_r = 5'd7 ; 
               6'b000110 : dout_5b_r = 5'd8 ; 
               6'b101000 : dout_5b_r = 5'd15;
               6'b100100 : dout_5b_r = 5'd16;
               6'b000101 : dout_5b_r = 5'd23;
               6'b001100 : dout_5b_r = 5'd24;
               6'b001001 : dout_5b_r = 5'd27;
               6'b010001 : dout_5b_r = 5'd29;
               6'b100001 : dout_5b_r = 5'd30;
               6'b010100 : dout_5b_r = 5'd31;
               6'b001111 : begin dout_5b_r = 5'd28; is_k28 = 1;end
               6'b110000 : begin dout_5b_r = 5'd28; is_k28 = 1;end
               default : begin dout_5b_r = 5'bXXXXX; is_k28 = 0;end
            endcase
    end

assign dout_8b = {dout_3b_r,dout_5b_r};
endmodule

四、仿真

testbench如下:

module tb_8bto10b(    );
    reg clk;
    reg rst_n;
    reg  [7:0] din_8b,din_8b_d0;
    reg is_k;
    wire error;
    wire [7:0] dout_8b;
    wire [9:0] dout_10b;
    
    integer i;
    
    initial begin
    clk = 0;rst_n = 1;din_8b='d0;is_k=1'b0;
    #100 rst_n=0;
    #100 rst_n=1;
    for(i=0;i<255;i=i+1)begin
        #10 din_8b = din_8b + 8'd1;
    end
    end
    always #5 clk = ~clk;
    always @(posedge clk) din_8b_d0 <= din_8b;
    assign error = (din_8b_d0 != dout_8b);
    encode_8bto10b encode_8bto10b(
    .clk        (clk     ),
    .rst_n      (rst_n   ),
    .din_en     (1'b1    ),
    .is_k       (is_k    ),     //是否为k码
    .din_8b     (din_8b  ),     //输入的8bit数据
    .dout_10b   (dout_10b)      //输出的10bit数据
    );
    
    decode_10bto8b decode_10bto8b(
        .clk            (clk     ),
        .rst_n          (rst_n   ),
        .din_10b        (dout_10b),
        .dout_8b        (dout_8b )
     );
endmodule

下图是数据编码
请添加图片描述

总结

  本文基于查找表实现了8b10b编码,资源不是最优,可以使用门电路来实现达到资源最优,还是那句话实现逻辑为主,该模块也为后面serdes的基础。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值