Menu Close

高速串行通信帧结构设计初步

上节内容讲解在接收端串并转换后需要捕获同步字,这在通信中是个广泛和通用的概念。例如UART需要起始位和停止位在异步串行通信中就起到了同步字的作用,以太网的 “前导字+帧起始”(Preamble +SFD)同样起到同步字的作用。其它还有在无线领域经常使用的调制方式如OFDM,就是利用自相关的特性进行同步字或同步信号的搜索。同步字可以在时域实现也可以在频域实现,根据不同的应用选择不同的方式。本章内容借鉴以太网MAC层对同步字的处理,在串行通信中插入Preamble+SFD的结构,从而初步实现二层结构的通信方式,通信结构的初步框架如图1所示:

图1

1. 各分层细节

由于本章内容采用循序渐进的方式逐步介绍通信中分层的概念,因此对于数据链路层的介绍也是由浅入深的方式逐步深入的。初步设计如下:

图2

一般在通信中把物理层划分为3个子层,每层各完成不同的任务,如以太网通信中把物理层分为PCS(Physical Coding Sublayer),PMA( Physical  Media Attachment),PMD (Physical Media Dependent)3个子层,关于这方面的内容参照网络通信相关的章节。在本章的异步串行通信中也分为PCS, PMA, PMD 3个子层。 其中PMA对应是并/串转换和串/并转换,PMD对应的是LVDS接口。

由于物理层中并/串(PMA子层),串/并转换(PMA子层),LVDS (PMD子层)以及数据链路层接收侧的同步字搜索,接收端的帧形成已经讲解,下面重点是设计应用层(APP)到数据链路层的实现。

 

2. 发送端数据链路层设计

继续在hs_txrx的工程文件下修改trans_data.v文件,增加握手信号tx_vld,tx_done。这样在应用层(如tb.v)文件里就不需要处理数据链路层的信息了。

 

图3

  • 修改链路层trans_data.v文件

新的trans_data.v文件如下:

module trans_data
#(
    parameter SYNC1 = 10'b1010_1010_10,
    parameter SYNC2 = 10'b1010_1010_11
)
(
    input rst,
    input clk_10M,
    input clk_100M,
    input tx_vld,           //新添加的数据有效信号
    input [ 9: 0 ] tx_data, // 10M的时钟对应的数据
    input tx_done,          //新添加包结束信号
    output tx_out
);



wire tx_in;
reg [ 9: 0 ] tx_dra;        // tx_data_ra   register a
reg [ 9: 0 ] tx_drb;        //tx_data_rb;
reg [ 9: 0 ] tx_dr;
reg tx_dr_en;
reg [ 9: 0 ] tx_sft;        //tx shift
//reg   [3:0]       sft_count;

reg cmp;


assign tx_in = tx_sft[ 9 ];

//================= new code ===========================

reg tx_vld_r;
reg tx_done_r;
reg [ 9: 0 ] lvds10_data_in;

reg [ 1: 0 ] sync_data_st;


always@( posedge clk_10M )
begin
    tx_done_r <= tx_done;
    tx_vld_r <= tx_vld;
end


always@( posedge clk_10M or posedge rst )

    if ( rst )
    begin
        lvds10_data_in <= SYNC1;
        sync_data_st <= 3'b0;
    end
    else
    case ( sync_data_st )
        0:
        begin
            lvds10_data_in <= SYNC1;

            if ( tx_vld )
            begin
                lvds10_data_in <= SYNC2;
                sync_data_st <= 3'd1;
            end
        end

        1:
        begin
            sync_data_st <= 3'd2;

            if ( tx_vld_r )
            begin
                lvds10_data_in <= tx_data;
            end
        end

        2:
        begin

            if ( tx_done_r )
            begin
                lvds10_data_in <= SYNC1;    //idle 空闲
                sync_data_st <= 3'd0;
            end
            else
            begin
                lvds10_data_in <= tx_data;
            end

        end

        default:
            sync_data_st <= 3'd0;

    endcase

//=============== new code =================




always@( posedge clk_100M or posedge rst )

    if ( rst )
    begin
        tx_dra <= 0;
        tx_drb <= 0;
        tx_dr <= 0;
        cmp <= 0;
    end
    else
    begin
        //tx_dra<=tx_data;
        tx_dra <= lvds10_data_in; //修改数据源
        tx_drb <= tx_dra;

        tx_dr_en <= 1'b0;

        cmp <= tx_dra != tx_drb;  //compare

        if ( cmp )
        begin

            tx_dr <= lvds10_data_in;

            tx_dr_en <= 1'b1;

        end

    end

//=============方法1:利用100Mhz时钟提取10M边沿脉冲,利用边沿脉冲采样10Mhz同步的数据,
//=============可以避开数据的变化区域进行采样
//reg   clk_10M_r;
//reg   nclk_10M_r;
//reg   clk_10M_rg;// rg---posedge
//reg  [9:0] tx_data_100M;
//reg            tx_data_100M_vld;
//always@(posedge clk_100M or posedge rst)
//if(rst) begin
//    clk_10M_r      <=0;
//    nclk_10M_r      <=1'b1;
//    clk_10M_rg      <=0;
//    tx_data_100M      <=0;
//    tx_data_100M_vld<=0;
//end
//else begin
//    clk_10M_r<=clk_10M;
//    nclk_10M_r<=~clk_10M_r;
//    clk_10M_rg<=clk_10M_r&nclk_10M_r;
//
//    tx_data_100M_vld<=1'b0;
//    if(clk_10M_rg) begin
//     tx_data_100M_vld<=1'b1;
//     tx_data_100M<=tx_data;
//end
//
//end
//==============


//===============方法2:利用循环移位实现在数据没有更新的前提下重复数据串行化

always@( posedge clk_100M or posedge rst )
    if ( rst )
        tx_sft <= 0;  //tx-shift
    else
    begin
        if ( tx_dr_en )
            tx_sft <= tx_dr;
        else
            tx_sft <= { tx_sft[ 8: 0 ], tx_sft[ 9 ] } ; //new  tx_sft.d[9]=old_tx_sft.q[8]

    end


//=================方法3:利用移位计数器,触发并行10位数据恢复

//always@(posedge clk_100M or posedge rst)
//if(rst) begin
// tx_sft        <=0;  //tx-shift
// sft_count<=0;
//end
//else begin
//if(tx_dr_en) begin
//tx_sft<=tx_dr;
//sft_count<=0;
//end
//
//else if(sft_count==9) begin
//tx_sft<=tx_dr;
//sft_count<=0;
//end
//else begin
//
//tx_sft<={tx_sft[8:0],1'b0} ; //new  tx_sft.d[9]=old_tx_sft.q[8]
//
//sft_count<=sft_count+1;
//end
//
//
//end


IO_TX IO_TX_inst
(
    .tx_in ( tx_in ),
    .tx_out ( tx_out )

);


endmodule

 

  • 修改仿真文件tb.v

在tb.v文件里处理好握手信号tx_vld和tx_done,同时将Preamble,SFD等信息移出,只实现用户数据与数据链路层的传递。添加包与包之间的间隔控制(inter-packet interval )

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


parameter PERIOD = 20 ;         //周期20
reg pll_rst;                    //实体模块PLL的rst信号
wire tx_rx;                     //收发在testbench的回环信号

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

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

wire rx_data_vld;
wire [ 9: 0 ] rx_data;
reg inclk ;                     //模拟板级提供的输入时钟

reg tx_vld;
reg tx_done;

reg [ 7: 0 ] frame_interval;    //frame interval , interval between two consecutive frame 帧间隔
reg [ 7: 0 ] frame_len;

reg [ 2: 0 ] tx_st;

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

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

//always #10 inclk=~inclk;

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

always@( posedge clk_10M or posedge rst )

    if ( rst )
    begin
        tx_vld <= 1'b0;
        tx_done <= 1'b0;
        frame_len <= 0;

        tx_data <= 0;
        frame_interval <= 0;

        tx_st <= 0;
    end
    else
    begin
        case ( tx_st )
            0:
            begin
                tx_vld <= 1'b0;
                tx_done <= 1'b0;
                tx_data <= 0;
                frame_interval <= frame_interval + 1;   //帧间隔+1
                if ( frame_interval == 15 )
                    tx_st <= 1;
            end
            1:
            begin
                frame_interval <= 0;
                frame_len <= 0;

                tx_vld <= 1'b1;
                tx_done <= 1'b0;
                tx_data <= 0;  
                tx_st <= 2;
            end
            2:
            begin
                frame_len <= frame_len + 1;
                tx_vld <= 1'b1;
                tx_data <= frame_len;

                if ( frame_len == 15 )
                begin
                    tx_st <= 3;
                end
            end
            3:
            begin
                tx_vld <= 1'b0;
                tx_done <= 1'b1;
                tx_data <= 8'h00;
                tx_st <= 0;
            end
            default:
                tx_st <= 0;

        endcase
    end


hs_txrx hs_txrx_inst
(
    .inclk      ( inclk ),
    .pll_rst    ( pll_rst ),
    .tx_vld     ( tx_vld ),
    .tx_done    ( tx_done ),
    .tx_data    ( tx_data ),
    .tx         ( tx_rx ),
    .rx         ( tx_rx ),
    .rx_data_vld( rx_data_vld ),
    .rx_data    ( rx_data ),
    .clk_10M    ( clk_10M ),
    .pll_locked ( pll_locked )

);
endmodule

 

  • 修改hs_txrx.v

主要目的是令新添加的控制信号能正确传递, 代码修改如下:

module hs_txrx
#(
    parameter SYNC1 = 10'b1010_1010_10,
    parameter SYNC2 = 10'b1010_1010_11
)
(
    input inclk,
    input pll_rst,
    
    input tx_vld,
    input tx_done,
    input [ 9: 0 ] tx_data,

    output tx,
    input rx,

    output rx_data_vld,
    output [ 9: 0 ] rx_data,

    output clk_10M,
    output pll_locked
);

wire clk_100M;
wire clk_400M;

wire rst = !pll_locked;


trans_data
#(
    .SYNC1( SYNC1 ),
    .SYNC2( SYNC2 )
)

trans_data_inst
(
    .rst        ( rst ),
    .clk_10M    ( clk_10M ),
    .clk_100M   ( clk_100M ),
    .tx_vld     ( tx_vld ),
    .tx_done    ( tx_done ),
    .tx_data    ( tx_data ),    // 10M的时钟对应的数据
    .tx_out     ( tx )
);



recv_data
#(
    .SYNC1( SYNC1 ),
    .SYNC2( SYNC2 )
)
recv_data_inst
(
    .rst            ( rst ),
    .clk_100M       ( clk_100M ),
    .clk_400M       ( clk_400M ),
    .rx             ( rx ),
    .rx_data        ( rx_data ),
    .rx_data_vld    ( rx_data_vld )

);





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

 );


endmodule

 

仿真波形下:

图4   tb.v仿真波形

 

从图4的波形和发送与接收对比看,已经成功完成数据包的正确收发。而且tb.v可以认为是应用层,因此只关心APP数据,将数据链路层和物理层需要的处理措施,交给底层处理。

图5  数据链路层波形(trans_data.v)

 

图6 接收端数据帧

从上面讨论的结果看,我们已经实现了三层协议,应用层,数据链路层和物理层。其中物理层又实现了PMA和PMD层。后续章节将会讲解物理层中控制子层PCS层的构建。

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

发表回复

相关链接