FPGA_WebLab/public/doc/12/doc.md

114 KiB
Raw Blame History

2.光纤通信

2.1 章节导读

在现代高速通信系统中,光纤通信作为一种重要的信息传输手段,广泛应用于数据中心、城域网、广域网以及各种嵌入式高速数据传输场景中。相比传统电缆传输,光纤具有带宽高、传输距离远、抗电磁干扰能力强、保密性好等显著优势,是实现高速、高可靠性通信的关键技术。

随着FPGA在通信领域的深入应用基于FPGA的光纤通信系统设计成为一项非常实用的技能。在本实验中我们将基于开发板配套的光纤接口模块设计并实现一个基础的光纤通信收发系统。通过本实验同学们将掌握光纤收发器SFP模块的基本使用方法了解高速串行通信8b10b编码等概念并学会如何通过FPGA完成数据的高速发送与接收。

2.2 理论学习

2.2.1 8B10B编码

8b/10b 编码常用于光纤通信和 LVDS 信号中,其主要目的是解决信号传输过程中的直流不平衡问题。由于串行电路通常采用交流耦合的方式(串接电容)我们知道理想电容的阻抗公式Zc=\frac{1}{2pif*C} 通过这个公式可以知道频率f越高阻抗越低反之频率越低阻抗越高。

无法显示图片时显示的文字
图1.交流耦合

如上图所示当码型是高频的时候基本可以不损耗的传输过去但是当码型为连续的0或者1的时候电容的损耗就很大导致幅度不断降低最严重的后果就是无法识别到底是0还是1因此8b/10b编码就是为了尽量把低频的码型优化成较高频率的码型从而降低阻抗带来的损耗。以光模块为例它只能传输“亮”或“不亮”两种状态即二进制的 1 和 0。当传输数据中出现长时间连续的 1 或 0如 111111100000000)时,线路中电容的损耗就会很大,从而无法准确采样,进而造成误判。

同样,在 LVDS 高速传输中也存在类似问题。如果传输的 0 和 1 数量长期不均衡,线路上的基准电压会发生偏移,影响信号的正确识别。

因此8b/10b 编码通过对每 8 位数据编码成 10 位信号实现传输数据的直流平衡和足够的码流转换也就是保证串行数据不会连续出现1和0从而提升系统的可靠性和抗干扰能力。

那么8b10b编码是如何将8bit数据编码成10bit数据的呢假设原始8位数据从高到低用HGFEDCBA表示将8位数据分成高3位HGF和低5位EDCBA两个子组。经过5B/6B编码将低5位EDCBA映射成abcdei高3位经过3B/4B编码映射成fghj最后合成abcdeifghj发送。

无法显示图片时显示的文字
图2.8b10b编码原理

可见8b10b编码是基于5b/6b和3b/4b之上的。

5B/6B code

input

RD = 1

RD = +1

input

RD = 1

RD = +1

EDCBA

abcdei

EDCBA

abcdei

D.00

00000

100111

011000

D.16

10000

011011

100100

D.01

00001

011101

100010

D.17

10001

100011

D.02

00010

101101

010010

D.18

10010

010011

D.03

00011

110001

D.19

10011

110010

D.04

00100

110101

001010

D.20

10100

001011

D.05

00101

101001

D.21

10101

101010

D.06

00110

011001

D.22

10110

011010

D.07

00111

111000

000111

D.23 †

10111

111010

000101

D.08

01000

111001

000110

D.24

11000

110011

001100

D.09

01001

100101

D.25

11001

100110

D.10

01010

010101

D.26

11010

010110

D.11

01011

110100

D.27 †

11011

110110

001001

D.12

01100

001101

D.28

11100

001110

D.13

01101

101100

D.29 †

11101

101110

010001

D.14

01110

011100

D.30 †

11110

011110

100001

D.15

01111

010111

101000

D.31

11111

101011

010100

K.28

11100

001111

110000

3b/4b code

input

RD = 1

RD = +1

input

RD = 1

RD = +1

HGF

fghj

HGF

fghj

D.x.0

000

1011

0100

K.x.0

000

1011

0100

D.x.1

001

1001

K.x.1 ‡

001

0110

1001

D.x.2

010

0101

K.x.2 ‡

010

1010

0101

D.x.3

011

1100

0011

K.x.3

011

1100

0011

D.x.4

100

1101

0010

K.x.4

100

1101

0010

D.x.5

101

1010

K.x.5 ‡

101

0101

1010

D.x.6

110

0110

K.x.6 ‡

110

1001

0110

D.x.P7 †

111

1110

0001

D.x.A7 †

111

0111

1000

K.x.7 † ‡

111

0111

1000

上述两张表格就是5b/6b和3b/4b编码表那我们先来看懂这两张表格。

首先是D.x.y将低5位EDCBA按其十进制数值记为x将高3位按其十进制数值记为y将原始8bit数据记为D.x.y。例如8bit数据最大为255也就是111_11111低五位是11111也就是十进制31高三位111是十进制7所对应的8b10b就是D.31.7。这也说明了上述5b/6b编码最大为D.313b/4b编码最大为D.x.7。

至于RD = - 1和RD = + 1。+ -分别表示0比1多1比0多多的个数是一样的而且最多多两个。当0和1一样多时便没有+1-1之分。编码的初始化状态是-1。

对于D.x.7† 当和5B/6B组合时D.x.P7和D.x.A7编码必须选择一个来避免连续的5个0或1。D.x.A7用在x=17 x=18 x=20当RD=-1时 x=11 x=13 x=14 当RD=+1时。 其他情况下x.A7码不能被使用。

表里面D.23D.27D.29D.30后面带 †它有自己的特殊配对规则当x=23 x=27 x=29 x=30时3b/4b这边使用K.x.7进行编码。

除了上面的一些编码8b10b编码还规定了一些特殊的字符也称为K码。K码表如下所示

控制符Control symbols

input

RD = 1

RD = +1

HGF EDCBA

abcdei fghj

abcdei fghj

K.28.0

000 11100

001111 0100

110000 1011

K.28.1 †

001 11100

001111 1001

110000 0110

K.28.2 

010 11100

001111 0101

110000 1010

K.28.3 

011 11100

001111 0011

110000 1100

K.28.4 

100 11100

001111 0010

110000 1101

K.28.5 †

101 11100

001111 1010

110000 0101

K.28.6 

110 11100

001111 0110

110000 1001

K.28.7 ‡

111 11100

001111 1000

110000 0111

K.23.7 

111 10111

111010 1000

000101 0111

K.27.7 

111 11011

110110 1000

001001 0111

K.29.7 

111 11101

101110 1000

010001 0111

K.30.7 

111 11110

011110 1000

100001 0111

8B/10B标准中使用了12个特殊的控制代码他们能在数据中被发送利用这些控制字符可以组成各种控制语句其中K.28.1 K.28.5 K.28.7被称为逗号字符,利用序列检测状态机检测逗号字符,以保证在串行数据接收过程中接收到数据的唯一性。如果没有逗号字符,串行接收设备将不知道数据是从哪一位开始,当序列检测器检测到逗号字符时就确定了字符的开始。

2.2.2 hsstlp ip核

实验板小眼睛盘古pg2l100h有2个hsst硬核共8路全双工高速串行收发接口每个接口最高支持6.6Gbps的速度其中4路用于PCIE2路用于SMA接口2路用于sfp光纤接口。有关hsstlp的内容可以参考官方手册。

无法显示图片时显示的文字
图3.HSSP ip核界面

2.3 实战演练

2.3.1实验目标

利用板载一路sfp光纤通道接收来自ctrlFPGA的数据实现光纤通信。

2.3.2硬件资源

实验板上有2路sfp接口全部与ctrl-FPGA相连。

无法显示图片时显示的文字
图4.板载sfp接口

2.3.3程序设计

首先我们应该学会配置并使用hsst ip核。

在ip核的配置界面可以看到可以配置4个通道的状态分别是全双工仅接收仅发送和不使能。同时还可以给每路通道选择不同的标准协议例如GEXAUI等勾选Protocol Default Setting后可以使用该协议的默认配置。也可以自定义协议传输。

在本次实验中我们将通道3设置成全双工自定义协议线速率5Gbps编码选择8b10b传输和接收的数据长度选择32位PLL的参考时钟为125M。配置图如下

无法显示图片时显示的文字
图5.hsst ip核

在Alignment界面可以配置对齐word alignment的模式对齐所用的K码多通道绑定channel bonding的模式以及高速传输时出现的时钟偏差补偿Clock Tolerance Compensation具体使用可以参考hsst手册。

本次实验配置字节对齐模式为用户自定义K码选择K28.5,不使用通道对齐和时钟偏差补偿。配置如下图所示:

无法显示图片时显示的文字
图6.hsst ip核

在Misc界面选择使用官方复位程序参考时钟27M板载时钟复位时钟个数为32767接收模式RX Termination Mode选择外部AC内部DC。配置如下图所示

无法显示图片时显示的文字
图7.hsst ip核

生成ip核后我们将ip核例化在顶层文件中目前配置的ip核包括复位所需要的信号重要的状态信号传输时钟i_p_tx3_clk_fr_core传输的32位数据i_txd_3传输的数据类型i_txk_3接收时钟i_p_rx3_clk_fr_core接收的32位数据o_rxd_3接收的数据类型o_rxk_3。这里解释一下txk和rxk这个信号是4位的每一位对应txd和rxd信号的8位数据1表示该8位数据是k码0表示改8位数据是普通数据。参考代码如下

module hsst_top (
    input clk,
    input rstn,
    input i_p_refckn_0,
    input i_p_refckp_0,
    output [7:0] led
);
wire i_free_clk;
wire i_pll_rst_0;
wire o_pll_done_0;
wire o_txlane_done_3;
wire o_rxlane_done_3;
wire o_p_clk2core_tx_3;
wire i_p_tx3_clk_fr_core;
wire o_p_clk2core_rx_3;
wire i_p_rx3_clk_fr_core;
wire o_p_pll_lock_0;
wire o_p_rx_sigdet_sta_3;
wire o_p_lx_cdr_align_3;
wire i_p_l3rxn;
wire i_p_l3rxp;
wire o_p_l3txn;
wire o_p_l3txp;
wire [3:0] i_tdispsel_3;
wire [3:0] i_tdispctrl_3;
wire [2:0] o_rxstatus_3;
wire [3:0] o_rdisper_3;
wire [3:0] o_rdecer_3;
wire txclk;
reg [31:0] i_txd_3;
reg [ 3:0] i_txk_3;
wire rxclk;
wire [31:0] o_rxd_3;
wire [ 3:0] o_rxk_3;
assign i_free_clk                = clk;
assign i_pll_rst_0               = rstn;
assign i_p_tx3_clk_fr_core       = o_p_clk2core_tx_3; 
assign i_p_rx3_clk_fr_core       = o_p_clk2core_rx_3;
assign txclk                     = i_p_tx3_clk_fr_core;
assign rxclk                     = i_p_rx3_clk_fr_core;
hsst the_instance_name (
  .i_free_clk               (i_free_clk         ), //复位序列参考时钟 
  .i_pll_rst_0              (i_pll_rst_0        ), //pll复位
  .i_wtchdg_clr_0           (),                    // 
  .o_wtchdg_st_0            (),                    //
  .i_p_refckn_0             (i_p_refckn_0       ), //pll参考差分时钟
  .i_p_refckp_0             (i_p_refckp_0       ), //
  .o_pll_done_0             (o_pll_done_0       ), //pll复位完成
  .o_txlane_done_3          (o_txlane_done_3    ), //tx通道初始化完成 
  .o_rxlane_done_3          (o_rxlane_done_3    ), //rx通道初始化完成
  .o_p_pll_lock_0           (o_p_pll_lock_0     ), //pll lock信号
  .o_p_rx_sigdet_sta_3      (o_p_rx_sigdet_sta_3), //sigdet_sta状态信号
  .o_p_lx_cdr_align_3       (o_p_lx_cdr_align_3 ), //cdr_align状态信号
  .o_p_clk2core_tx_3        (o_p_clk2core_tx_3  ), //output传输时钟
  .i_p_tx3_clk_fr_core      (i_p_tx3_clk_fr_core), //input
  .o_p_clk2core_rx_3        (o_p_clk2core_rx_3  ), //output接收时钟
  .i_p_rx3_clk_fr_core      (i_p_rx3_clk_fr_core), //input
  .i_p_pcs_word_align_en_3  (1'b1               ), //使能word_align
  .i_p_l3rxn                (i_p_l3rxn          ), //差分数据线
  .i_p_l3rxp                (i_p_l3rxp          ), //差分数据线
  .o_p_l3txn                (o_p_l3txn          ), //差分数据线
  .o_p_l3txp                (o_p_l3txp          ), //差分数据线
  .i_txd_3                  (i_txd_3            ), //传输的32位数据
  .i_tdispsel_3             (i_tdispsel_3       ), //
  .i_tdispctrl_3            (i_tdispctrl_3      ), //
  .i_txk_3                  (i_txk_3            ), //传输的数据类型0普通数据1K码
  .o_rxstatus_3             (o_rxstatus_3       ), //
  .o_rxd_3                  (o_rxd_3            ), //接收的32位数据
  .o_rdisper_3              (o_rdisper_3        ), //
  .o_rdecer_3               (o_rdecer_3         ), //
  .o_rxk_3                  (o_rxk_3            )  //接收的数据类型0普通数据1K码
);
//*******************************************************************//
assign led = {1'b0,1'b0,o_pll_done_0,o_p_pll_lock_0,o_txlane_done_3,o_p_rx_sigdet_sta_3, o_p_lx_cdr_align_3,o_rxlane_done_3};

reg [ 2:0] tx_state;
reg [15:0] txcnt;
wire tx_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)begin
        i_txd_3 <= 32'hBCBCBCBC;
        i_txk_3 <=  4'b1111;
    end
    else if(tx_state == 0)begin
        i_txd_3 <= 32'hBCBCBCBC;
        i_txk_3 <=  4'b1111;
    end
    else if(tx_state == 1)begin
        i_txd_3 <= 32'h00000000;
        i_txk_3 <=  4'b0000;
    end
    else if(tx_state == 2)begin
        i_txd_3 <= i_txd_3 + 1;
        i_txk_3 <=  4'b0000;
    end
end
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)begin
        tx_state <= 0;
    end
    else if(tx_state == 0)begin
        if(txcnt == 7)
            tx_state <= 1;
        else 
            tx_state <= 0;
    end
    else if(tx_state == 1)begin
        tx_state <= 2;
    end
    else if(tx_state == 2)begin
        if(txcnt == 1032)
            tx_state <= 0;
        else 
            tx_state <= 2;
    end
end
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)
        txcnt <= 0;
    else if(txcnt == 1032)
        txcnt <= 0;
    else 
        txcnt <= txcnt + 1;
end

endmodule

到现在为止我们已经可以接收和发送数据了但使用的时候会发现一个问题如下图所示我们发送的数据是0xc5bcc5bc,但接收到的数据却是0xbcc5bc5数据出现了移位这是为什么呢这是因为发送编码是8b10b是以8位数据为一个基本单位进行发送的虽然发送数据是0xc5bcc5bc但是在串行通信中他并不知道你发送的32位数据是以c5开头还是以bc开头前面在ip核中设置的word alignment正确的对齐了BCk28.5这个字符正确的接收了字节数据但32位数据中字节顺序需要我们自己去调整。因为设置ip时用的自定义模式

无法显示图片时显示的文字
图8.debugger抓取波形
为了解决这一问题我们也要给他设计一个模块使他自动实现接收数据与发送数据一致不会出现错位。在本实验中空闲时发送逗号字符K28.5 BC有数据要发送时发送32位数据。根据此格式设计对齐模块参考代码如下
module Word_Alignment_32bit (
    input wire          clk             ,
    input wire          rstn            ,
    input wire [31:0]   data_bf_align   /* synthesis PAP_MARK_DEBUG="1" */,
    input wire [ 3:0]   rxk             /* synthesis PAP_MARK_DEBUG="1" */,
    output reg          data_valid      /* synthesis PAP_MARK_DEBUG="1" */,
    output reg [31:0]   data_af_align   /* synthesis PAP_MARK_DEBUG="1" */,
    output reg          data_done/* synthesis PAP_MARK_DEBUG="1" */
);
//************************ 8b10b    K_Code ********************************************
//K28.0     1C
//K28.1     3C
//K28.2     5C
//K28.3     7C
//K28.4     9C
//K28.5     BC
//K28.6     DC
//K28.7     FC
//K23.7     F7
//K27.7     FB
//K29.7     FD
//K30.7     FE
//********************** Pattern Controller ********************************************
// txdata format
// data_x = {data_x_1,data_x_2,data_x_3,data_x_4}
// 假如发送数据是data_1data_2data_3
// 接收数据很可能会出现
// {data_1}
//
//
//
//
//
//
// data Format:
// 
// __ ________ ________ ________ ________          ________ ________ ________ ________ ________
// __X_ idle__X__idle__X__idle__X__data__x ……………… x__data__X__idle__X__idle__X__idle__X___idle_
// 
// idle   <=  K28.5
// 空闲时发送K28.5,有数据时发送数据,
// 本模块根据以上格式实现32位数据对齐使得接收数据与发送数据一致不会出现错位。
// 
// 
//**************************************************************************************

//parameter
localparam IDLE   = 0;
localparam ALIGN1 = 1;
localparam ALIGN2 = 2;
localparam ALIGN3 = 3;
localparam ALIGN4 = 4;
reg [4:0] state;
reg [4:0] nextstate;
reg skip;
reg rxcnt;
reg [ 7:0] datareg8;
reg [15:0] datareg16;
reg [23:0] datareg24;
reg [31:0] datareg32;
reg error;
// always @(negedge clk or negedge rstn)begin
//     if(~rstn)begin
//         datareg8       <= 0;
//         datareg16      <= 0;
//         datareg24      <= 0;
//         datareg32      <= 0;
//     end
//     else begin
//         datareg8  <= data_bf_align[31:24];
//         datareg16 <= data_bf_align[31:16];
//         datareg24 <= data_bf_align[31:8];
//         datareg32 <= data_bf_align;
//     end
// end
always @(posedge clk or negedge rstn)begin
    if(~rstn)begin
        state <= IDLE;
    end
    else begin
        state <= nextstate;
    end
end
always @(*)begin
    case(state)
        IDLE : begin
            if     (rxk == 4'b0111) nextstate <= ALIGN1;
            else if(rxk == 4'b0011) nextstate <= ALIGN2;
            else if(rxk == 4'b0001) nextstate <= ALIGN3;
            else if(rxk == 4'b0000) nextstate <= ALIGN4;
            else                    nextstate <= IDLE; 
        end
        ALIGN1 : begin
            if(skip || error) begin
                if     (rxk == 4'b0111) nextstate <= ALIGN1;
                else if(rxk == 4'b0011) nextstate <= ALIGN2;
                else if(rxk == 4'b0001) nextstate <= ALIGN3;
                else if(rxk == 4'b0000) nextstate <= ALIGN4;
                else                    nextstate <= IDLE; 
            end
            else 
                nextstate <= ALIGN1; 
        end
        ALIGN2 : begin
            if(skip || error) begin
                if     (rxk == 4'b0111) nextstate <= ALIGN1;
                else if(rxk == 4'b0011) nextstate <= ALIGN2;
                else if(rxk == 4'b0001) nextstate <= ALIGN3;
                else if(rxk == 4'b0000) nextstate <= ALIGN4;
                else                    nextstate <= IDLE; 
            end
            else 
                nextstate <= ALIGN2; 
        end
        ALIGN3 : begin
            if(skip || error) begin
                if     (rxk == 4'b0111) nextstate <= ALIGN1;
                else if(rxk == 4'b0011) nextstate <= ALIGN2;
                else if(rxk == 4'b0001) nextstate <= ALIGN3;
                else if(rxk == 4'b0000) nextstate <= ALIGN4;
                else                    nextstate <= IDLE; 
            end
            else 
                nextstate <= ALIGN3; 
        end
        ALIGN4 : begin
            if(skip || error) begin
                if     (rxk == 4'b0111) nextstate <= ALIGN1;
                else if(rxk == 4'b0011) nextstate <= ALIGN2;
                else if(rxk == 4'b0001) nextstate <= ALIGN3;
                else if(rxk == 4'b0000) nextstate <= ALIGN4;
                else                    nextstate <= IDLE; 
            end
            // if(skip)
            //     nextstate <= IDLE;
            // else if(error)
            //     nextstate <= IDLE;
            else 
                nextstate <= ALIGN4; 
        end
    endcase
end
always @(posedge clk or negedge rstn) begin
    if(~rstn)begin
        data_valid     <= 0;
        data_af_align  <= 0;
        data_done      <= 0; 
        rxcnt          <= 0;
        skip           <= 0;
        datareg8       <= 0;
        datareg16      <= 0;
        datareg24      <= 0;
        datareg32      <= 0;
        error          <= 0;
    end
    else begin
        // data_valid <= 0;
        // data_done <= 0;
        case(state)
            IDLE : begin
                rxcnt <= 0; 
                skip <= 0;
                data_valid <= 0;
                data_done <= 0;
                if(rxk == 4'b1111)begin
                    data_af_align <= data_bf_align;
                    error <= 0;
                end
                else begin
                    if(rxk == 4'b0111)begin
                        datareg8  <= data_bf_align[31:24];
                        error <= 0;
                    end
                    else if(rxk == 4'b0011)begin
                        datareg16 <= data_bf_align[31:16];
                        error <= 0;
                    end
                    else if(rxk == 4'b0001)begin
                       datareg24 <= data_bf_align[31: 8];
                       error <= 0;
                    end
                    else if(rxk == 4'b0000)begin
                       datareg32 <= data_bf_align;
                       error <= 0;
                    end
                    else begin
                        error <= 1;
                    end
                end
            end 
            ALIGN1 : begin
                data_af_align <= {data_bf_align[23:0],datareg8};
                datareg8 <= data_bf_align[31:24];
                if(skip) skip <= 0;
                else if(rxk == 4'b1000) skip <= 1;
                else if(rxk == 4'b0000) skip <= 0;
                else error <= 1;
                
                if(skip)                data_done <= 0;
                else if(rxk == 4'b1000) data_done <= 1;
                else                    data_done <= 0;

                if(skip)  data_valid <= 0;
                else      data_valid <= 1;
            end
            ALIGN2 : begin
                data_af_align <= {data_bf_align[15:0],datareg16};
                datareg16 <= data_bf_align[31:16];
                if(skip) skip <= 0;
                else if(rxk == 4'b1100) skip <= 1;
                else if(rxk == 4'b0000) skip <= 0;
                else error <= 1;
                
                if(skip)                data_done <= 0;
                else if(rxk == 4'b1100) data_done <= 1;
                else                    data_done <= 0;

                if(skip)  data_valid <= 0;
                else      data_valid <= 1;
            end
            ALIGN3 : begin
                data_af_align <= {data_bf_align[7:0],datareg24};
                datareg24 <= data_bf_align[31:8];
                if(skip) skip <= 0;
                else if(rxk == 4'b1110) skip <= 1;
                else if(rxk == 4'b0000) skip <= 0;
                else error <= 1;
                
                if(skip)                data_done <= 0;
                else if(rxk == 4'b1110) data_done <= 1;
                else                    data_done <= 0;

                if(skip)  data_valid <= 0;
                else      data_valid <= 1;
            end
            ALIGN4 : begin
                data_af_align[31:0] <= datareg32;
                datareg32 <= data_bf_align;

                if(skip) skip <= 0;
                else if(rxk == 4'b1111) skip <= 1;
                else if(rxk == 4'b0000) skip <= 0;
                else error <= 1;
                
                if(skip)                data_done <= 0;
                else if(rxk == 4'b1111) data_done <= 1;
                else                    data_done <= 0;

                if(skip)  data_valid <= 0;
                else      data_valid <= 1;
            end
        endcase
    end
end

endmodule

我们将对齐模块例化到顶层并且为了观察接收数据与发送数据是否一致我们将接收到的数据从0开始比较如果相同led0就会亮如果不同led0就不亮同时把未经32位对齐模块的数据与发送数据对比相同led1亮不同led1不亮新的顶层模块如下所示

module hsst_top (
    input clk,
    input rstn,
    input i_p_refckn_0,
    input i_p_refckp_0,
    output [7:0] led
);
wire i_free_clk;
wire i_pll_rst_0;
wire o_pll_done_0;
wire o_txlane_done_3;
wire o_rxlane_done_3;
wire o_p_clk2core_tx_3;
wire i_p_tx3_clk_fr_core;
wire o_p_clk2core_rx_3;
wire i_p_rx3_clk_fr_core;
wire o_p_pll_lock_0;
wire o_p_rx_sigdet_sta_3;
wire o_p_lx_cdr_align_3;
wire i_p_l3rxn;
wire i_p_l3rxp;
wire o_p_l3txn;
wire o_p_l3txp;
wire [3:0] i_tdispsel_3;
wire [3:0] i_tdispctrl_3;
wire [2:0] o_rxstatus_3;
wire [3:0] o_rdisper_3;
wire [3:0] o_rdecer_3;
wire txclk;
reg [31:0] i_txd_3;
reg [ 3:0] i_txk_3;
wire rxclk;
wire [31:0] o_rxd_3;
wire [ 3:0] o_rxk_3;
assign i_free_clk                = clk;
assign i_pll_rst_0               = rstn;
assign i_p_tx3_clk_fr_core       = o_p_clk2core_tx_3; 
assign i_p_rx3_clk_fr_core       = o_p_clk2core_rx_3;
assign txclk                     = i_p_tx3_clk_fr_core;
assign rxclk                     = i_p_rx3_clk_fr_core;
hsst the_instance_name (
  .i_free_clk               (i_free_clk         ), //复位序列参考时钟 
  .i_pll_rst_0              (i_pll_rst_0        ), //pll复位
  .i_wtchdg_clr_0           (),                    // 
  .o_wtchdg_st_0            (),                    //
  .i_p_refckn_0             (i_p_refckn_0       ), //pll参考差分时钟
  .i_p_refckp_0             (i_p_refckp_0       ), //
  .o_pll_done_0             (o_pll_done_0       ), //pll复位完成
  .o_txlane_done_3          (o_txlane_done_3    ), //tx通道初始化完成 
  .o_rxlane_done_3          (o_rxlane_done_3    ), //rx通道初始化完成
  .o_p_pll_lock_0           (o_p_pll_lock_0     ), //pll lock信号
  .o_p_rx_sigdet_sta_3      (o_p_rx_sigdet_sta_3), //sigdet_sta状态信号
  .o_p_lx_cdr_align_3       (o_p_lx_cdr_align_3 ), //cdr_align状态信号
  .o_p_clk2core_tx_3        (o_p_clk2core_tx_3  ), //output传输时钟
  .i_p_tx3_clk_fr_core      (i_p_tx3_clk_fr_core), //input
  .o_p_clk2core_rx_3        (o_p_clk2core_rx_3  ), //output接收时钟
  .i_p_rx3_clk_fr_core      (i_p_rx3_clk_fr_core), //input
  .i_p_pcs_word_align_en_3  (1'b1               ), //使能word_align
  .i_p_l3rxn                (i_p_l3rxn          ), //差分数据线
  .i_p_l3rxp                (i_p_l3rxp          ), //差分数据线
  .o_p_l3txn                (o_p_l3txn          ), //差分数据线
  .o_p_l3txp                (o_p_l3txp          ), //差分数据线
  .i_txd_3                  (i_txd_3            ), //传输的32位数据
  .i_tdispsel_3             (i_tdispsel_3       ), //
  .i_tdispctrl_3            (i_tdispctrl_3      ), //
  .i_txk_3                  (i_txk_3            ), //传输的数据类型0普通数据1K码
  .o_rxstatus_3             (o_rxstatus_3       ), //
  .o_rxd_3                  (o_rxd_3            ), //接收的32位数据
  .o_rdisper_3              (o_rdisper_3        ), //
  .o_rdecer_3               (o_rdecer_3         ), //
  .o_rxk_3                  (o_rxk_3            )  //接收的数据类型0普通数据1K码
);
//*******************************************************************//

reg [ 2:0] tx_state;
reg [15:0] txcnt;
wire tx_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)begin
        i_txd_3 <= 32'hBCBCBCBC;
        i_txk_3 <=  4'b1111;
    end
    else if(tx_state == 0)begin
        i_txd_3 <= 32'hBCBCBCBC;
        i_txk_3 <=  4'b1111;
    end
    else if(tx_state == 1)begin
        i_txd_3 <= 32'h00000000;
        i_txk_3 <=  4'b0000;
    end
    else if(tx_state == 2)begin
        i_txd_3 <= i_txd_3 + 1;
        i_txk_3 <=  4'b0000;
    end
end
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)begin
        tx_state <= 0;
    end
    else if(tx_state == 0)begin
        if(txcnt == 7)
            tx_state <= 1;
        else 
            tx_state <= 0;
    end
    else if(tx_state == 1)begin
        tx_state <= 2;
    end
    else if(tx_state == 2)begin
        if(txcnt == 1032)
            tx_state <= 0;
        else 
            tx_state <= 2;
    end
end
always @(posedge txclk or negedge tx_rstn)begin
    if(~tx_rstn)
        txcnt <= 0;
    else if(txcnt == 1032)
        txcnt <= 0;
    else 
        txcnt <= txcnt + 1;
end
wire Word_Alignment_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
wire data_valid;
wire [31:0] data_af_align;
wire data_last;
Word_Alignment_32bit  Word_Alignment_32bit_inst (
    .clk            (rxclk              ),
    .rstn           (Word_Alignment_rstn),
    .data_bf_align  (o_rxd_3            ),
    .rxk            (o_rxk_3            ),
    .data_valid     (data_valid         ),
    .data_af_align  (data_af_align      ),
    .data_done      (data_last          )
  );
//**********************************//

reg [31:0] data_bf_Alignment_judge;
reg [31:0] data_af_Alignment_judge;

always@(posedge rxclk or negedge Word_Alignment_rstn)begin
    if(~Word_Alignment_rstn)data_bf_Alignment_judge <= 0;
    else if(o_rxk_3 == 4'b0000) data_bf_Alignment_judge <= data_bf_Alignment_judge + 1;
    else data_bf_Alignment_judge <= 0;
end
always@(posedge rxclk or negedge Word_Alignment_rstn)begin
    if(~Word_Alignment_rstn)data_af_Alignment_judge <= 0;
    else if(data_valid) data_af_Alignment_judge <= data_af_Alignment_judge + 1;
    else data_af_Alignment_judge <= 0;
end

assign led = {o_pll_done_0,o_p_pll_lock_0,o_txlane_done_3,o_rxlane_done_3,1'b0,1'b0,data_af_Alignment_judge == data_af_align,data_bf_Alignment_judge == o_rxd_3};
endmodule

2.3.4仿真验证

本实验不进行仿真可以点击工程目录下ipcore文件夹下hsst参考设计中sim文件夹的sim.bat文件进行仿真。

2.3.5上板验证

上板前要先进行管脚约束。端口除了板载时钟复位以及led还需要添加以下约束

define_attribute {p:i_p_refckn_0} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:i_p_refckn_0} {PAP_IO_LOC} {AB11}
define_attribute {p:i_p_refckn_0} {PAP_IO_UNUSED} {TRUE}
define_attribute {p:i_p_refckp_0} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:i_p_refckp_0} {PAP_IO_LOC} {AA11}
define_attribute {p:i_p_refckp_0} {PAP_IO_UNUSED} {TRUE}

define_attribute {i:hsst_inst.U_GTP_HSSTLP_WRAPPER.CHANNEL3_ENABLE.U_GTP_HSSTLP_LANE3} {PAP_LOC} {HSSTLP_364_0:U3_HSSTLP_LANE}
define_attribute {i:hsst_inst.U_GTP_HSSTLP_WRAPPER.PLL0_ENABLE.U_GTP_HSSTLP_PLL0} {PAP_LOC} {HSSTLP_364_0:U0_HSSTLP_PLL}

该语句主要约束hsst参考时钟管脚hsst所使用PLLPLL0以及hsst使用的通道。其他约束可以参考工程目录下ipcore文件夹中hsst内的参考例程。

完成管脚分配之后就可以生成sbit文件将文件提交到网站后点击烧录即可将sbit下载到实验板中在摄像头页面即可观察到8个led中前四个亮后四个分别为不亮不亮不亮的状态。前四个亮表示hsst中pll和rxtx通道初始化成功最后两个ledled1亮表示接收的与发送的数据相同led0不亮表示未经32位对齐模块处理的接收数据与发送数据不同。

2.4 章末总结

本次实验主要学习了高速串行通信的相关知识初步学会使用hsst ip核。