Menu Close

高速通信分层设计及PCS子层实现—发送端Verilog代码

在之前的几节内容中我们研究了通信分层的概念和分层实现的优点,其中在物理层中有一个非常重要的子层PCS(Physical Coding Sublayer)。该层主要有两个功能:

(1)通过编码实现改善信道的质量,如改善直流平衡,提供足够多的时钟信息等;

(2)提供物理层通信控制功能,实现高层协议无法完成的或不需要高层参与完成的功能。如物理层的自动协商,实现物理层差异时的自动适配,如速率适配,线序调换等功能。

本节内容以8b/10b编码编码框架实现PCS子层,工程代码也做了较大修改充分体现了高速通信中分层的概念。高速通信收发层次如图1所示。

图1

由于整个工程的代码层次比较多,本节内容着重以发送端的代码介绍为主,接收端的代码安排在下节内容实现。在本节内容除了PCS子层代码外,其它内容及实现方法已经介绍,但为了通信工程的层次更加清晰,各层代码也做了较大修改,同时应注重研究在同步时钟域下各层握手的实现方法。

tx握手信号如图2所示。

图2-1 tx握手信号

图2-2 tx握手信号

1. 应用层(tb_8b10b.v)

该层在本阶段作为通信部分研究的最高层,其它的通信协议会在后续的章节中作为扩展部分介绍。

`timescale 1 ns / 1 ps
module tb_8b10b
       (
       );

parameter LEN = 16;
parameter PERIOD = 20 ;    //周期20
parameter IDLE = 10'b1010_1010_10;

reg inclk ;       // 模拟板级提供的输入时钟

initial
begin
    inclk = 1'b0;
    #( PERIOD / 2 );

    forever
        #( PERIOD / 2 ) inclk = ~inclk;
end





reg pll_rst;     //实体模块PLL的rst信号
wire tx_rx;      // 收发在testbench的回环信号

wire clk_100M;
wire pll_locked;    //实体PLL锁定信号
wire rst = !pll_locked;     //Testbench内生成复位信号

reg [ 7: 0 ] tx_data;      //待发送数据


wire rx_data_vld;
wire rx_data_rdy;
wire ko;
wire [ 7: 0 ] rx_data;

wire tx_rdy;
reg tx_vld;
reg tx_en;
wire tx_vld_ack;

reg [ 7: 0 ] frame_len;

reg [ 2: 0 ] tx_st;



initial
begin
    pll_rst = 1'b1;
    #50 pll_rst = 1'b0;
end



always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin

        tx_vld <= 1'b0;
        tx_en <= 1'b0;
        frame_len <= 0;

        tx_data <= 0;

        tx_st <= 0;

    end
    else
    begin
        case ( tx_st )
            0:
            begin
                tx_vld <= 1'b0;
                tx_en <= 1'b0;
                tx_data <= 0;
                frame_len <= 0;
                tx_st <= 1;
            end

            1:
            begin
                tx_en <= 1'b0;
                tx_vld <= 1'b1;

                if ( frame_len == LEN )
                begin
                    tx_vld <= 1'b0;
                    tx_st <= 3;
                end
                else if ( tx_rdy )
                begin

                    tx_en <= 1'b1;
                    tx_st <= 2;
                end

            end

            2:
            begin
                tx_en <= 1'b0;
                frame_len <= frame_len + 1;
                tx_data <= tx_data + 1;
                tx_st <= 1;
            end

            3:
            begin
                frame_len <= 0;
                tx_vld <= 1'b0;
                tx_en <= 1'b0;

                if ( tx_vld_ack )
                    tx_st <= 1;
            end

            default:
                tx_st <= 0;

        endcase
    end



hs_txrx  #( .IDLE( IDLE ) )
hs_txrx_inst
(
    .inclk      ( inclk ),
    .pll_rst    ( pll_rst ),

    .tx_rdy     ( tx_rdy ),
    .tx_vld     ( tx_vld ),
    .tx_data    ( tx_data ),
    .tx_en      ( tx_en ),
    .tx_vld_ack ( tx_vld_ack ),

    .tx         ( tx_rx ),
    .rx         ( tx_rx ),
    .rx_data_vld( rx_data_vld ),
    .rx_data_rdy( rx_data_rdy ),
    .ko         ( ko ),
    .rx_data    ( rx_data ),
    .clk_100M   ( clk_100M ),
    .pll_locked ( pll_locked )

);
endmodule

 

图3  tb_8b10b.v 仿真波形1

图4 tb_8b10b.v 仿真波形2

2. 高速通信的顶层文件

module hs_txrx
#(
    parameter IDLE = 10'b1010_1010_10
)
(
    input inclk,
    input pll_rst,

    output tx_rdy,
    input tx_vld,
    input [ 7: 0 ] tx_data,
    input tx_en,
    output tx_vld_ack,

    output tx,
    input rx,


    output rx_data_vld,
    output rx_data_rdy,
    output ko,
    output [ 7: 0 ] rx_data,

    output clk_100M,
    output pll_locked
);


wire clk_400M;
wire rst = !pll_locked;


trans_data_link
#(
    .IDLE       ( IDLE )
)
trans_data_link_inst
(
    .rst        ( rst ),
    .clk_100M   ( clk_100M ),
    .tx_rdy     ( tx_rdy ),
    .tx_vld     ( tx_vld ),
    .tx_data    ( tx_data ),
    .tx_en      ( tx_en ),
    .tx_vld_ack ( tx_vld_ack ),
    .tx_out     ( tx )
);

recv_data_pcs #
(
    .SYNC_START1    ( 10'b0011111010 ),       //K28.5 with dipin=0;
    .SYNC_START2    ( 10'b1100000101 ),        //K28.5 with dispin=1

    .SYNC_END1      ( 10'b0011111001 ),       //K28.5 with dipin=0;
    .SYNC_END2      ( 10'b1100000110 )        //K28.5 with dispin=1
)
recv_data_pcs_inst
(
    .rst            ( rst ),
    .clk_100M       ( clk_100M ),
    .clk_400M       ( clk_400M ),
    .rx             ( rx ),
    .pcs_rx_data_rdy ( rx_data_rdy ),
    .pcs_ko         ( ko ),
    .pcs_rx_data    ( rx_data ),
    .pcs_rx_data_vld ( rx_data_vld )
);

pll1 pll1_inst
(
    .areset     ( pll_rst ),    //1'b0;
    .inclk0     ( inclk ),
    .c0         ( clk_100M ),
    .c1         ( clk_400M ),
    .locked     ( pll_locked )

);


endmodule

 

3.发送端数据链路层代码

数据链路层(data-link)的上层是应用层,下层为物理层,直接对接的是物理层的PCS子层。注意上下层之间的握手。同时该层包间隔的设定,为了简化设计,Peamble+SFD部分没有插入,感兴趣的读者可以自己尝试。

module trans_data_link
#(
    parameter IDLE = 10'b1010_1010_10
)
(
    input rst,
    input clk_100M,
    output reg tx_rdy,
    input tx_vld,
    input [ 7: 0 ] tx_data,
    input tx_en,
    output reg tx_vld_ack,
    output tx_out
);


localparam INTER_PAC_INTERVAL= 96;  //packect interval

wire tx_phy_rdy;
reg tx_phy_rdy_r;
reg tx_phy_rdy_ack;

always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin
        tx_phy_rdy_r <= 0;

    end
    else
    begin
        if ( tx_phy_rdy )
            tx_phy_rdy_r <= 1'b1;
        else if ( tx_phy_rdy_ack )
            tx_phy_rdy_r <= 1'b0;

    end


reg [ 7: 0 ] tx_phy_data;
reg tx_phy_en;
reg tx_phy_vld;


reg [ 7: 0 ] pac_interval;

reg [ 2: 0 ] link_st;

always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin

        pac_interval <= 0;
        tx_rdy <= 0;

        tx_phy_data <= 0;
        tx_phy_vld <= 0;
        tx_phy_en <= 0;
        tx_phy_rdy_ack <= 0;

        tx_vld_ack <= 0;
        link_st <= 0;
    end
    else
    case ( link_st )
        0:
        begin

            tx_phy_data <= 0;
            tx_phy_vld <= 0;
            tx_phy_en <= 0;
            tx_vld_ack <= 0;

            if ( pac_interval == INTER_PAC_INTERVAL)
                link_st <= 1;
            else
                pac_interval <= pac_interval + 1;
        end

        1:
        begin
            pac_interval <= 0;

            if ( tx_vld )
            begin
                tx_phy_vld <= 1'b1;

            end

            if ( tx_phy_rdy )
            begin
                tx_rdy <= 1'b1;
                link_st <= 2;
            end
        end

        2:
        begin
            tx_rdy <= 1'b0;

            if ( tx_en )
            begin
                tx_phy_data <= tx_data;
                link_st <= 3;
            end
        end

        3:
        begin
            if ( tx_phy_rdy_r )
            begin
                tx_phy_en <= 1'b1;
                tx_phy_rdy_ack <= 1'b1;
                link_st <= 4;
            end
        end

        4:
        begin
            tx_phy_en <= 1'b0;
            tx_phy_rdy_ack <= 1'b0;

            if ( tx_vld )
            begin
                tx_rdy <= 1'b1;
                link_st <= 2;
            end
            else
            begin
                tx_vld_ack <= 1'b1;
                link_st <= 0;
            end
        end

        default:
            link_st <= 0;

    endcase

tx_data_phy
#(
    .IDLE( IDLE )
)
tx_data_phy_inst
(
    .rst        ( rst ),
    .clk_100M   ( clk_100M ),
    .tx_phy_rdy ( tx_phy_rdy ),
    .tx_phy_vld ( tx_phy_vld ),
    .tx_phy_en  ( tx_phy_en ),
    .tx_phy_data ( tx_phy_data ),
    .tx_out     ( tx_out )
);


endmodule

 

图5  trans_data_link.v 握手信号波形

图5中,应注重观察 data-link 层与应用层及PCS的握手,同时分析包间隔(frame interval)的插入。

4.发送端PCS子层代码

本层的上层是数据链路层,下层为PMA子层,应注意上下层之间的握手。PCS子层采用8b/10b编码实现。其中采用k28.5作为帧起始,K28.1作为帧结束。8b/10b编码部分见 文章 Verilog 8b/10b encoder.

图6

/*
  physcal layer :
  (1) first layer of physical, PCS sublayer
  (2) PCS controled through 8b/10b encode 
  (3) 
  
  engineer: Tim zhuang
  company : Fraser innovation Inc.
  
*/


module tx_data_phy
#(
    parameter IDLE = 10'b10_1010_1010
)
(
    input rst,
    input clk_100M,
    input tx_phy_vld,
    output reg tx_phy_rdy,
    input tx_phy_en,
    input [ 7: 0 ] tx_phy_data,
    output tx_out
);


reg ki = 0;
reg [ 7: 0 ] datain;
reg dispin;

wire [ 9: 0 ] dataout;

wire dispout;


reg tx_pcs_en;
reg [ 9: 0 ] tx_pcs_data;
reg tx_pcs_rdy;


reg [ 2: 0 ] pcs_st;

always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin
        ki <= 0;
        datain <= 0;
        dispin <= 0;
        tx_pcs_en <= 0;
        tx_pcs_data <= 0;

        pcs_st <= 0;
    end
    else
    case ( pcs_st )

        0:
        begin
            ki <= 0;
            datain <= 0;
            dispin <= 0;
            tx_pcs_en <= 0;
            tx_pcs_data <= 0;

            tx_phy_rdy <= 1'b0;

            if ( tx_phy_vld )
            begin
                ki <= 1;
                datain <= 8'hBC;   //K28.5
                pcs_st <= 1;
            end
        end

        1:
        begin
            tx_pcs_data <= dataout;
            tx_phy_rdy <= 1'b0;
            tx_pcs_en <= 0;

            if ( tx_pcs_rdy )
            begin
                tx_phy_rdy <= tx_phy_vld;
                tx_pcs_en <= 1;
                pcs_st <= 2;
            end
        end

        2:
        begin
            tx_phy_rdy <= 1'b0;
            tx_pcs_en <= 0;

            if ( ~tx_phy_vld )
                pcs_st <= 3;

            if ( tx_phy_en )
            begin
                ki <= 0;
                datain <= tx_phy_data;
                dispin <= dispout;
                pcs_st <= 1;
            end
        end

        3:
        begin
            ki <= 1;
            datain <= 8'h3C;   //K28.1
            dispin <= dispout;
            pcs_st <= 4;
        end

        4:
        begin

            tx_pcs_data <= dataout;
            tx_phy_rdy <= 1'b0;
            tx_pcs_en <= 0;

            if ( tx_pcs_rdy )
            begin
                tx_pcs_en <= 1;
                pcs_st <= 0;
            end

        end

    endcase

encode_8b10b encode_8b10b_inst
(
    .ki         ( ki ),
    .datain     ( datain ),
    .dispin     ( dispin ),
    .dataout    ( dataout ),
    .dispout    ( dispout )
) ;


reg [ 9: 0 ] tx_pcs_data_r;
reg pcs_data_vld;

wire tx_pma_rdy;
reg [ 9: 0 ] tx_pma_data;
reg tx_pma_en;

always@( posedge clk_100M or posedge rst )
    if ( rst )
    begin
        tx_pma_en <= 1'b0;
        tx_pma_data <= IDLE;
        tx_pcs_data_r <= IDLE;
        tx_pcs_rdy <= 0;
        pcs_data_vld <= 0;
    end
    else
    begin

        if ( tx_pcs_en )
        begin
            tx_pcs_data_r <= tx_pcs_data;
            pcs_data_vld <= 1'b1;
        end
        else
            tx_pcs_rdy <= 1'b0;
            
        tx_pma_en <= 1'b0;
        if ( tx_pma_rdy )
        begin
            pcs_data_vld <= 1'b0;
            tx_pma_en <= 1'b1;
            tx_pcs_rdy <= 1'b1;

            if ( pcs_data_vld )
                tx_pma_data <= tx_pcs_data_r;
            else
                tx_pma_data <= 10'b10_1010_1010;
        end
    end


tx_PMA tx_PMA_INST
(
    .rst        ( rst ),
    .clk_100M   ( clk_100M ),
    .tx_pma_en  ( tx_pma_en ),
    .tx_pma_data( tx_pma_data ),
    .tx_pma_rdy ( tx_pma_rdy ),
    .tx_out     ( tx_out )

);


endmodule

 

图7 PCS层结构图

图8 tx_data_phy.v(PCS)仿真波形1

图8中应着重分析与上层 data-link以及下层PMA 层的握手,同时结合本层的程序,研究如何插入K28.5.

图9 tx_data_phy.v(PCS)仿真波形2

利用图9波形结合PCS层代码分析包结束部分的握手,以及如何插入K28.1控制字符。

5.发送端 PMA代码

本层主要实现并串转换,要注意与上层PCS子层的握手。

module tx_PMA
(
    input rst,
    input clk_100M,
    input tx_pma_en,
    input [ 9: 0 ] tx_pma_data,
    output reg tx_pma_rdy,
    output tx_out
);


wire tx_in;

reg [ 9: 0 ] tx_pma_data_r;
reg [ 9: 0 ] tx_sft;
reg [ 1: 0 ] tx_pma_st;

reg [ 3: 0 ] sft_count;



assign tx_in = tx_sft[ 9 ];

always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin
        tx_sft <= 0;
        tx_pma_st <= 0;
        sft_count <= 0;
        tx_pma_data_r <= 0;
        tx_pma_rdy <= 0;
    end
    else
    begin

        if ( tx_pma_en )
        begin
            tx_pma_data_r <= tx_pma_data;
        end

        tx_pma_rdy <= 0;

        if ( sft_count == 9 )
        begin
            tx_pma_rdy <= 1'b1;
            tx_sft <= tx_pma_data_r;
            sft_count <= 0;
        end
        else
        begin
            sft_count <= sft_count + 1;
            tx_sft <= { tx_sft[ 8: 0 ], 1'b0 };  //for debug convenience purpose
        end
    end

tx_PMD tx_PMD_inst
(
    .tx_in ( tx_in ),
    .tx_out( tx_out )
);

endmodule

 

图10  tx_pma.v 波形

在图10中注意观察握手,以及上层如何调度IDLE和数据包的。

6. 发送端PMD代码

本层采用LVDS实现PMD(Physical Media Dependent),物理媒介可以是PCB板的差分线,或双绞线。

module tx_PMD
(
    input tx_in,
    output tx_out
);



IO_TX IO_TX_INST
(
    .tx_in  ( tx_in ),
    .tx_out ( tx_out )
);

endmodule

以上6个部分的设计细节,由于过于繁琐,这里没有一一介绍,感兴趣的同学可以观看对应的视频。

Posted in FPGA, FPGA 教材教案, IC, IP开发, 教材与教案, 文章

发表回复

相关链接