整理Aurora8B10B官方示例工程(高速收发器十八)

发布于:2024-06-11 ⋅ 阅读:(39) ⋅ 点赞:(0)

1、概述

  前文对官方示例工程进行了仿真和上板测试,初步了解了如何使用该IP收发数据,但是官方的示例工程写的比较繁琐,如果想要使用多通道收发器该怎么办?多次例化示例工程?显然无法达到效果,因为示例工程中包含多个收发器的共享逻辑,多次例化后在综合时会报错。

  整理前的IP模块RTL视图如下所示,包括GT_COMMON等共享逻辑,不方便扩展收发器通道。

在这里插入图片描述

图1 整理前的IP模块连接

  整理后的模块如下图所示,共享逻辑部分位于收发器的顶层模块中,需要例化多个高速收发器时,只需要多次例化通道部分即可,多个高速收发器共用同一个QPLL。

在这里插入图片描述

图2 整理后的高速收发器RTL视图

  因此后文先整理该部分内容,将一些需要用到的信号引出,不会的输入信号接固定电平,输出信号悬空。然后本文调用两个收发器,然后对工程仿真和上板测试。这种整理思路适用于后续所有高速收发器相关IP,通过整理官方示例工程得到用户自己的工程。

2、整理示例工程

  首先整理每个通道的独立逻辑,然后再整理共享逻辑部分。如下图所示,单个通道包含以下三部分,首先是将IP输出的参考时钟信号通过BUFG生成用户时钟。

  然后例化复位同步模块,将两个异步复位通道到相应时钟域之下,作为IP的复位信号,最后例化aurora_8b10b IP。

在这里插入图片描述

图3 整理收发器独立逻辑部分

  aurora_8b10b IP的例化如下图所示,把必须使用和可能会使用到的信号引到上层模块,不会使用的输出信号悬空处理,不会使用的输入接无效的固定电平。

  Axi_steram、状态信号、部分时钟和复位信号、回环控制等信号必须引出,QPLL相关信号需要引到上层连接共享逻辑部分,而DRP信号后续开发可能会用到,因此也引出到上层模块。一般不会使用掉电模式,掉电信号接地即可。

在这里插入图片描述

图4 例化aurora_8b10b IP

  高速收发器的顶层模块如下所示,首先通过IBUFDS_GTE2把差分时钟转换为单端时钟信号,之后例化GT_COMMON模块,将各个收发器的QPLL相关信号与该模块相连接。

在这里插入图片描述

图5 高速收发器顶层模块

  整理后的高速收发器顶层RTL视图如下所示,例化了两个高速收发器模块,两个高速收发器共用同一个参考时钟信号和QPLL共享逻辑。需要注意只有一个高速收发器才能复位QPLL,其他收发器的QPLL复位信号悬空。

在这里插入图片描述

图6 整理后的高速收发器顶层RTL视图

  经过上述处理,完成了封装aurora_8b10b IP,两个收发器的顶层模块端口信号如下图所示。用户只需要通过axi_stream接口收发数据即可,一些状态信号可以判断高速收发器的工作状态,便于调试。

在这里插入图片描述

图7 高速收发器顶层信号

  为了测试整理后的模块,需要一个用于生成测试信号的模块,相关代码如下所示,每经过一段时间就会生成一组axi_stream数据用于测试,可以通过参数KEEP控制最后一个数据的有效字节。

    //时钟计数器,初始值为0,之后对时钟计数;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            cnt <= 'd0;
        end
        else if(m_axi_tx_last && m_axi_tx_valid)begin
            cnt <= 'd0;//发送完一帧数据清零。
        end
        else if((~m_axi_tx_valid) || (m_axi_tx_valid && m_axi_tx_ready))begin
            cnt <= cnt + 'd1;//计数空闲时间或者发送数据的时钟。
        end
    end

    //发送数据有效指示信号,初始值为0;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axi_tx_valid <= 1'b0;
        end
        else if(m_axi_tx_last && m_axi_tx_ready && m_axi_tx_valid)begin//发送最后一个后拉低;
            m_axi_tx_valid <= 1'b0;
        end
        else if(cnt == 2)begin//当计数器计数到127时发送一帧数据,此时拉高;
            m_axi_tx_valid <= 1'b1;
        end
    end

    //生成测试数据;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axi_tx_data <= 32'd0;
        end
        else if(m_axi_tx_last && m_axi_tx_valid)begin//当发送完最后一个数据时清零;
            m_axi_tx_data <= 32'd0;
        end
        else if(m_axi_tx_ready && m_axi_tx_valid)begin
            m_axi_tx_data <= {{cnt[5:0],2'd0},{{cnt[5:0],2'd0}+8'd1},{{cnt[5:0],2'd0}+8'd2},{{cnt[5:0],2'd0}+8'd3}};
        end
    end
    
    //一帧数据最后一个指示信号,初始值为0;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axi_tx_last <= 1'b0;
            m_axi_tx_keep <= 4'b1111;
        end
        else if(m_axi_tx_last && m_axi_tx_ready)begin
            m_axi_tx_last <= 1'b0;
            m_axi_tx_keep <= 4'b1111;//最后一个数据发送完成之后回到设置值。
        end
        else if(m_axi_tx_valid && m_axi_tx_ready && (cnt == DATA_LEN))begin//当发送最后一个数据时拉高,其余时间保持不变;
            m_axi_tx_last <= 1'b1;
            m_axi_tx_keep <= KEEP;//最后一个数据输出对应掩码信号。
        end
    end

  最后顶层模块的RTL视图如下所示,两个数据生成模块分别给两个高速收发器生成数据。

在这里插入图片描述

图8 顶层模块RTL

3、工程仿真

  对应的仿真激励文件如下所示:

    assign gt_0_rx_p = gt_1_tx_p;
    assign gt_0_rx_n = gt_1_tx_n;
    assign gt_1_rx_p = gt_0_tx_p;
    assign gt_1_rx_n = gt_0_tx_n;

    top u_top(
        .clk		    ( clk		    ),//系统时钟信号;
        .rst_n	        ( rst_n	        ),//系统复位信号,低电平有效;
        .gt_refclk_p    ( gt_refclk_p   ),//gt差分参考时钟信号;
        .gt_refclk_n    ( gt_refclk_n   ),//gt差分参考时钟信号;
        .gt_0_rx_p      ( gt_0_rx_p     ),//gt接收差分数据线;
        .gt_0_rx_n      ( gt_0_rx_n     ),//gt接收差分数据线;
        .gt_0_tx_p      ( gt_0_tx_p     ),//gt发送差分数据线;
        .gt_0_tx_n      ( gt_0_tx_n     ),//gt发送差分数据线;
        .gt_1_rx_p      ( gt_1_rx_p     ),//gt接收差分数据线;
        .gt_1_rx_n      ( gt_1_rx_n     ),//gt接收差分数据线;
        .gt_1_tx_p      ( gt_1_tx_p     ),//gt发送差分数据线;
        .gt_1_tx_n      ( gt_1_tx_n     ),//gt发送差分数据线;
        .dislabe        ( dislabe       ) //光电转换模块失能信号;
    );

    //生成周期为CYCLE数值的系统时钟;
    initial begin
        clk = 0;
        forever #(CYCLE/2) clk = ~clk;
    end

    //生成165.25MHz的差分时钟信号;
    initial begin
        gt_refclk_p = 1;
        forever #3.2 gt_refclk_p = ~gt_refclk_p;
    end
    initial begin
        gt_refclk_n = 0;
        forever #3.2 gt_refclk_n = ~gt_refclk_n;
    end

    //生成复位信号;
    initial begin
        rst_n = 1;
        #2;
        rst_n = 0;//开始时复位10个时钟;
        #(RST_TIME*CYCLE);
        rst_n = 1;
    end

  仿真需要运行大约40us后IP才能初始化完成,才能正常收发数据,如下所示。

在这里插入图片描述

图9 整体仿真时序

  两个数据生成模块发送的数据长度是不一样的,通过下图进行设置,可以对多种可能性进行仿真测试。

在这里插入图片描述

图10 两个数据生成模块

  仿真结果如下所示,通道0和通道1相互发送数据,其中通道1接收的数据与通道0发送的数据一致,通道1发送的数据也与通道0接收的数据一致,由此证明该IP仿真正确。

在这里插入图片描述

图11 两通道的仿真结果

4、上板测试

  上述模块仿真完成之后,分配工程的端口信号,打开ILA模块的注释,然后综合工程。开发板有两个光口,对应的原理图如下所示。

在这里插入图片描述

图12 光口原理图

开发板使用光纤短接两个光口,如下图所示。

在这里插入图片描述

图13 开发板光口连接

  工程综合完成之后,下载到开发板,使用ILA抓取相关信号。如下图所示lane_up和channel_up均为高电平,表示IP已经初始化完成的。

  下图是高速收发器0的相关信号,红色框中的信号是发送通道需要发送的数据,总共发送了4个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、16’h1415。

在这里插入图片描述

图14 高速收发器0发送数据时序

  下图是高速收发器1接收数据的时序,接收一帧4个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、16’h1415,与高速收发器0发送的数据一样。

在这里插入图片描述

图15 高速收发器1接收数据时序

下图是高速收发器1发送数据的时序,一帧发送5个数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、32’h114151617、8’h18。

在这里插入图片描述

图16 高速收发器1发送数据时序

  下图是高速收发器0接收数据的时序,接收的一帧长度为5的数据,分别为32’h0000、32’h0c0d0e0f、32’h10111213、32’h114151617、8’h18,与高速收发器1发送的数据保持一致。

在这里插入图片描述

图17 高速收发器0接收数据时序

  上述ILA抓取的时序证明高速收发器的接收功能和发送功能都没有问题,本文封装的双通道收发器功能正常。

  本文讲解到此结束,后续只要涉及到高速收发器相关IP都可以采用类似的方式进行封装,便于扩展高速收发器。


  如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!

  如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!