当前位置: 首页 > news >正文

整理及仿真Xilinx的SRIO示例工程(高速收发器三十)

  点击进入高速收发器系列文章导航界面


  书接上N回,由于我对SRIO协议本身其实还不是特别了解,所以SRIO这部分一直没有继续写,这不CSDN官方的2024年度博客之星评选活动来了么。由于工作的几个项目都在最近回来了,根本没时间去写东西,不得不发以前的东西了。

  2024年还是写了很多东西,2025年第一季度工作实在太忙,博客应该会从第二季度后持续更新。先打个广告,如果认为我的博客有帮助,最近这几天(2.14~2.20闭区间)可以帮我在CSDN的博客之星评选中投一下票,谢谢大家。

  投票链接:https://www.csdn.net/blogstar2024/detail/225

  同时谢谢已经为我投票的各位,下个季度开始会持续更新,最后可能会补齐一季度缺失的。

在这里插入图片描述

1、示例工程仿真

  运行示例工程的仿真,结果如下所示,仿真大约需要140us才能跑完,需要耐心等待一会。

  在收发数据之前,维护端口会先通过axi4_lite总线配置内部寄存器,之后才开始通信。

在这里插入图片描述

图1 总体仿真结果

  下图是复位时序,各个复位信号在各自时钟域下有效时间均大于4个时钟周期,符合前文的复位时序要求。

在这里插入图片描述

图2 复位时序

  当port_initialized和link_initialized均拉高时,表示该IP复位初始化完成。如果在调试时这两个信号没有拉高,则应该查看时钟是否锁定,复位时序是否正常。

在这里插入图片描述

图3 初始化成功时序

  高速收发器0向高速收发器1发起第一个事务的时序如下所示。

在这里插入图片描述

图4 流写事务时序

  上图中包头为64’h006020fc_d0000600,参考下图Hello数据包格式可知,该帧头表示时优先级为1,长度为16字节的流写操作时序,发送的16字节数据为64’h00000000_00000000和64’h01010101_01010101。

在这里插入图片描述

图5 Hello数据包格式

  由于流写操作不需要应答,因此在应答通道的数据有效指示信号并不会拉高,如下图所示。

在这里插入图片描述

图6 流写的应答通道无有效数据

  由于在仿真时,例化了两个示例工程模块短接进行仿真,因此在实际仿真时,两个高速收发器的时序是一致的,两个高速收发器会通过各自的ireq向对方发起事务请求,因此只需要关注一个高速收发器的时序即可。

  因为上述原因,在ireq发起的事务请求,会出现在treq端口上,如果该请求需要响应,则会通过tresp发出,然后响应数据包出现在iresp上。

  如下图所示,发起不需要响应的事务请求后会出现在接收事务请求通道上,如果发起需要响应的事务请求,则接收事务请求后,会返回响应信息,仿真的前一段时间发起的事务请求都是不需要响应的。

在这里插入图片描述

图7 事务请求

  下图连续发送两个需要响应的请求事务时序,帧头数据为64’h25552050_04550002,后续发出的数据为64’hafafafaf_afafafaf。

在这里插入图片描述

图8 带响应的写请求事务

  通过下表可知上数据包是带响应的写请求事务,接收端需要应答该数据包。

在这里插入图片描述

图9 Hello数据包格式

  在接收事务响应端口接收到上述数据包和数据,然后在响应通道发送一个无数据的响应数据包。

在这里插入图片描述

图10 响应时序

  通过以上方式查看示例工程的其余仿真时序即可,不再赘述。

2、整理示例工程

  与前文一样,为了便于后续扩展,应该将官方的示例工程进行整理,将共享逻辑放到顶层模块中,便于后续扩展高速收发器。

  通道模块包含时钟生成模块、复位模块以及SRIO的IP,对应的RTL视图如下所示。

在这里插入图片描述

图11 SRIO通道模块

  整理过程可以参考官方示例工程,整理后的参考代码如下所示:

    assign user_clk = log_clk;//生成用户时钟信号;
    assign user_rst = log_rst;//生成用户复位信号;
    assign gt_qpll_rst = gt_pcs_rst;//根据示例工程,将PCS复位作为QPLL复位;
    assign gt_qplllockdetclk = drpclk;

    //例化时钟生成模块;
    srio_gen2_0_srio_clk u_srio_gen2_0_srio_clk(
        .gt_refclk      ( gt_refclk     ),//GT参考时钟信号;
        .sys_rst        ( sys_rst       ),//系统复位信号;
        .mode_1x        ( mode_1x       ),//表示链路已经训练到1个高速收发器通道。
        .log_clk        ( log_clk       ),//逻辑部分时钟信号;
        .phy_clk        ( phy_clk       ),//PHY时钟信号,是高速收发器参考时钟的四分之一。
        .gt_pcs_clk     ( gt_pcs_clk    ),//高速收发器PCS时钟信号,是gt_clk的一半;
        .gt_clk         ( gt_clk        ),//用于高速收发器的时钟;
        .drpclk         ( drpclk        ),//配置时钟信号;
        .clk_lock       ( clk_lock      ) //时钟锁定信号;
    );

    //例化复位生成模块;
    srio_gen2_0_srio_rst u_srio_gen2_0_srio_rst(
        .cfg_clk                    ( log_clk               ),//配置端口时钟信号;
        .log_clk                    ( log_clk               ),//逻辑部分时钟信号;
        .phy_clk                    ( phy_clk               ),//PHY时钟信号,是高速收发器参考时钟的四分之一。
        .gt_pcs_clk                 ( gt_pcs_clk            ),//高速收发器PCS时钟信号,是gt_clk的一半;
        .sys_rst                    ( sys_rst               ),//系统复位信号;
        .port_initialized           ( port_initialized      ),//表示PHY已经收到至少四个连续的、无错误的链路复位控制符号。
        .phy_rcvd_link_reset        ( phy_rcvd_link_reset   ),//
        .force_reinit               ( 1'b0                  ),//
        .clk_lock                   ( clk_lock              ),//时钟锁定信号;
        .controlled_force_reinit    ( force_reinit          ),//该信号强制PHY重新初始化链路。
        .cfg_rst                    ( cfg_rst               ),//配置端口复位信号;
        .log_rst                    ( log_rst               ),//逻辑部分复位信号;
        .buf_rst                    ( buf_rst               ),//buf缓冲区复位信号;
        .phy_rst                    ( phy_rst               ),//PHY复位信号;
        .gt_pcs_rst                 ( gt_pcs_rst            ) //高速收发器PCS复位信号。
    );

    //例化SRIO IP;
    srio_gen2_0 u_srio_gen2_0 (
        .gt_clk_in                      ( gt_clk                ),//用于高速收发器的时钟;
        .drpclk_in                      ( drpclk                ),//配置时钟信号;
        .refclk_in                      ( gt_refclk             ),//参考时钟信号;
        .log_clk_in                     ( log_clk               ),//逻辑部分时钟信号;
        .log_rst_in                     ( log_rst               ),//逻辑部分复位信号;
        .phy_clk_in                     ( phy_clk               ),//PHY时钟信号,是高速收发器参考时钟的四分之一。
        .phy_rst_in                     ( phy_rst               ),//PHY复位信号;
        .gt_pcs_clk_in                  ( gt_pcs_clk            ),//高速收发器PCS时钟信号,是gt_clk的一半;
        .gt_pcs_rst_in                  ( gt_pcs_rst            ),//高速收发器PCS复位信号。
        .clk_lock_in                    ( clk_lock              ),//时钟锁定信号;
        .buf_rst_in                     ( buf_rst               ),//buf缓冲区复位信号;
        .cfg_rst_in                     ( cfg_rst               ),//配置端口复位信号;
        .deviceid                       ( deviceid              ),//存储在基本设备ID CSR中的当前值(偏移量0x60)。
        .port_decode_error              ( port_decode_error     ),//高电平表示收到了不支持的数据包,当接收到支持的数据包时拉低。
        .s_axis_ireq_tvalid             ( s_axis_ireq_tvalid    ),//用户发送数据端口的数据请求的AXI数据有效指示信号;
        .s_axis_ireq_tready             ( s_axis_ireq_tready    ),//用户发送数据端口的数据请求的AXI数据应答信号;
        .s_axis_ireq_tlast              ( s_axis_ireq_tlast     ),//用户发送数据端口的数据请求的AXI最后一个数据有效指示信号;
        .s_axis_ireq_tdata              ( s_axis_ireq_tdata     ),//用户发送数据端口的数据请求的AXI数据信号;
        .s_axis_ireq_tkeep              ( s_axis_ireq_tkeep     ),//用户发送数据端口的数据请求的AXI数据掩码信号;
        .s_axis_ireq_tuser              ( s_axis_ireq_tuser     ),//用户发送数据端口的数据请求的AXI的源ID和目的ID信号;
        .m_axis_iresp_tvalid            ( m_axis_iresp_tvalid   ),//用户发送数据端口的数据响应的AXI数据有效指示信号;
        .m_axis_iresp_tready            ( m_axis_iresp_tready   ),//用户发送数据端口的数据响应的AXI数据应答信号;
        .m_axis_iresp_tlast             ( m_axis_iresp_tlast    ),//用户发送数据端口的数据响应的AXI最后一个数据有效指示信号;
        .m_axis_iresp_tdata             ( m_axis_iresp_tdata    ),//用户发送数据端口的数据响应的AXI数据信号;
        .m_axis_iresp_tkeep             ( m_axis_iresp_tkeep    ),//用户发送数据端口的数据响应的AXI数据掩码信号;
        .m_axis_iresp_tuser             ( m_axis_iresp_tuser    ),//用户发送数据端口的数据响应的AXI的源ID和目的ID信号;
        .m_axis_treq_tvalid             ( m_axis_treq_tvalid    ),//用户接收数据端口的数据请求的AXI数据有效指示信号;
        .m_axis_treq_tready             ( m_axis_treq_tready    ),//用户接收数据端口的数据请求的AXI数据应答信号;
        .m_axis_treq_tlast              ( m_axis_treq_tlast     ),//用户接收数据端口的数据请求的AXI最后一个数据有效指示信号;
        .m_axis_treq_tdata              ( m_axis_treq_tdata     ),//用户接收数据端口的数据请求的AXI数据信号;
        .m_axis_treq_tkeep              ( m_axis_treq_tkeep     ),//用户接收数据端口的数据请求的AXI数据掩码信号;
        .m_axis_treq_tuser              ( m_axis_treq_tuser     ),//用户接收数据端口的数据请求的AXI的源ID和目的ID信号;
        .s_axis_tresp_tvalid            ( s_axis_tresp_tvalid   ),//用户接收数据端口的数据响应的AXI数据有效指示信号;
        .s_axis_tresp_tready            ( s_axis_tresp_tready   ),//用户接收数据端口的数据响应的AXI数据应答信号;
        .s_axis_tresp_tlast             ( s_axis_tresp_tlast    ),//用户接收数据端口的数据响应的AXI最后一个数据有效指示信号;
        .s_axis_tresp_tdata             ( s_axis_tresp_tdata    ),//用户接收数据端口的数据响应的AXI数据信号;
        .s_axis_tresp_tkeep             ( s_axis_tresp_tkeep    ),//用户接收数据端口的数据响应的AXI数据掩码信号;
        .s_axis_tresp_tuser             ( s_axis_tresp_tuser    ),//用户接收数据端口的数据响应的AXI的源ID和目的ID信号;
        .s_axi_maintr_rst               ( s_axi_maintr_rst      ),//维护端口的axi_lite复位信号;
        .s_axi_maintr_awvalid           ( s_axi_maintr_awvalid  ),//维护端口的axi_lite写地址有效指示信号;
        .s_axi_maintr_awready           ( s_axi_maintr_awready  ),//维护端口的axi_lite写地址应答信号;
        .s_axi_maintr_awaddr            ( s_axi_maintr_awaddr   ),//维护端口的axi_lite写地址信号;
        .s_axi_maintr_wvalid            ( s_axi_maintr_wvalid   ),//维护端口的axi_lite写数据有效指示信号;
        .s_axi_maintr_wready            ( s_axi_maintr_wready   ),//维护端口的axi_lite写数据应答信号;
        .s_axi_maintr_wdata             ( s_axi_maintr_wdata    ),//维护端口的axi_lite写数据信号;
        .s_axi_maintr_bvalid            ( s_axi_maintr_bvalid   ),//维护端口的axi_lite写数据应答指示信号;
        .s_axi_maintr_bready            ( s_axi_maintr_bready   ),//维护端口的axi_lite写数据应答通道的应答信号;
        .s_axi_maintr_bresp             ( s_axi_maintr_bresp    ),//维护端口的axi_lite写数据的应答状态信号;
        .s_axi_maintr_arvalid           ( s_axi_maintr_arvalid  ),//维护端口的axi_lite读地址有效指示信号;
        .s_axi_maintr_arready           ( s_axi_maintr_arready  ),//维护端口的axi_lite读地址应答信号;
        .s_axi_maintr_araddr            ( s_axi_maintr_araddr   ),//维护端口的axi_lite读地址信号;
        .s_axi_maintr_rvalid            ( s_axi_maintr_rvalid   ),//维护端口的axi_lite读数据有效指示信号;
        .s_axi_maintr_rready            ( s_axi_maintr_rready   ),//维护端口的axi_lite读数据应答信号;
        .s_axi_maintr_rdata             ( s_axi_maintr_rdata    ),//维护端口的axi_lite读数据信号;
        .s_axi_maintr_rresp             ( s_axi_maintr_rresp    ),//维护端口的axi_lite读数据应答状态信号;
        .buf_lcl_response_only_out      (                       ),//该信号表示发送缓冲器只能接受响应。
        .buf_lcl_tx_flow_control_out    (                       ),//表示发送缓冲器处于发送器控制的流量控制模式。
        .idle2_selected                 (                       ),//拉高表示PHY在IDLE2模式下工作并使用长控制符号(因此ackIDs为6位)。
        .idle_selected                  (                       ),//高电平表示表示已选择IDLE1或IDLE2。
        .buf_lcl_phy_buf_stat_out       (                       ),//用于表示接收缓冲器的状态。
        .gt0_qpll_clk_in                ( gt_qpll_clk           ),//QPLL的时钟信号。
        .gt0_qpll_out_refclk_in         ( gt_qpll_out_refclk    ),//QPLL的参考时钟信号。
        .gt0_qpll_lock_in               ( gt_qpll_lock          ),//QPLL的时钟锁定信号。
        .sim_train_en                   ( sim_train_en          ),//高电平用于加快仿真速度。
        .phy_mce                        ( 1'b0                  ),//用于指示PHY发送MCE控制信号的单周期输入信号。
        .phy_link_reset                 ( 1'b0                  ),//只要该信号被拉高,PHY就发送链路复位控制符号。
        .force_reinit                   ( force_reinit          ),//该信号强制PHY重新初始化链路。
        .phy_lcl_phy_next_fm_out        (                       ),//用于传送由ackID索引的下一个要发送的数据包。
        .phy_lcl_phy_last_ack_out       (                       ),//用于传送接收到的最后一个应答控制符号的ackID。
        .phy_lcl_phy_rewind_out         (                       ),//高电平表示PHY正在请求倒带。
        .link_initialized               (                       ),//高电平表示链接已初始化。
        .phy_lcl_phy_rcvd_buf_stat_out  (                       ),//用于传送从对方接收的状态控制符号中最后接收的缓冲状态。
        .phy_rcvd_mce                   (                       ),//表示PHY已从对方接收到MCE控制符号。
        .phy_rcvd_link_reset            ( phy_rcvd_link_reset   ),//表示PHY已经收到至少四个连续的、无错误的链路复位控制符号。
        .port_error                     ( port_error            ),//表示端口收到不可恢复的错误,并处于错误状态。
        .port_initialized               ( port_initialized      ),//表示端口已经初始化。
        .mode_1x                        ( mode_1x               ),//表示链路已经训练到1个高速收发器通道。
        .port_timeout                   (                       ),//反映端口链路超时控制CSR中超时值字段的值。
        .srio_host                      (                       ),//反映物理层配置中端口通用控制CSR的主机位的值。
        .phy_lcl_master_enable_out      (                       ),//高电平表示BUF可以向PHY转发请求,低电平表示BUF只能转发响应。
        .phy_lcl_maint_only_out         (                       ),//高电平表示逻辑层应该只传输维护事务。
        .gtrx_disperr_or                ( gtrx_disperr          ),//高电平表示字节对齐错误。
        .gtrx_notintable_or             ( gtrx_notintable       ),//高电平表示8B/10B解码错误。
        .phy_debug                      (                       ),//低205位表示各种对应的错误,可用于调试。
        .srio_txn0                      ( srio_tx_n             ),//SRIO的发送数据差分信号;
        .srio_txp0                      ( srio_tx_p             ),//SRIO的发送数据差分信号;
        .srio_rxn0                      ( srio_rx_n             ),//SRIO的接收数据差分信号;
        .srio_rxp0                      ( srio_rx_p             ) //SRIO的接收数据差分信号;
    );

  在上层调用两个通道模块,之后外部使用光纤回环测试IP通信,虽然该工程并不会使用QPLL,但是必须例化GT_COMMON,这是GTX手册中规定的,只要使用高速收发器,就必须例化QPLL。

在这里插入图片描述

图12 高速收发器的模块

  该模块的参考代码如下所示:

    //例化差分时钟转单端模块;
    IBUFDS_GTE2 u_IBUFDS_GTE2(
        .O      ( gt_refclk     ),//GT单端参考时钟信号;
        .I      ( gt_refclk_p   ),//GT差分时钟信号;
        .IB     ( gt_refclk_n   ),//GT差分时钟信号;
        .CEB    ( 1'b0          ),//差分转单端使能信号;
        .ODIV2  (               ) //GT单端参考时钟二分频信号;
    );

    //例化QPLL模块;
    srio_gen2_0_k7_v7_gtxe2_common u_srio_gen2_0_k7_v7_gtxe2_common(
        .gt0_gtrefclk0_common_in( gt_refclk             ),//GT单端参考时钟信号;
        .gt0_qplllockdetclk_in  ( gt_qplllockdetclk     ),//
        .gt0_qpllreset_in       ( gt_qpll_rst           ),//
        .qpll_clk_out           ( gt_qpll_clk           ),//
        .qpll_out_refclk_out    ( gt_qpll_out_refclk    ),//
        .gt0_qpll_lock_out      ( gt_qpll_lock          ) //
    );  

    //例化通道0的SRIO IP
    srio_channel u_srio_channel_0(
        .gt_refclk              ( gt_refclk             ),//参考时钟信号;
        .sys_rst                ( sys_rst               ),//系统复位信号;
        .user_clk               ( user_clk_0            ),//生成用户时钟信号;
        .user_rst               ( user_rst_0            ),//生成用户复位信号;
        .deviceid               (                       ),//存储在基本设备ID CSR中的当前值(偏移量0x60)。
        .port_decode_error      (                       ),//高电平表示收到了不支持的数据包,当接收到支持的数据包时拉低
        .s_axis_ireq_tvalid     ( s0_axis_ireq_tvalid   ),//用户发送数据端口的数据请求的AXI数据有效指示信号;
        .s_axis_ireq_tready     ( s0_axis_ireq_tready   ),//用户发送数据端口的数据请求的AXI数据应答信号;
        .s_axis_ireq_tlast      ( s0_axis_ireq_tlast    ),//用户发送数据端口的数据请求的AXI最后一个数据有效指示信号;
        .s_axis_ireq_tdata      ( s0_axis_ireq_tdata    ),//用户发送数据端口的数据请求的AXI数据信号;
        .s_axis_ireq_tkeep      ( s0_axis_ireq_tkeep    ),//用户发送数据端口的数据请求的AXI数据掩码信号;
        .s_axis_ireq_tuser      ( s0_axis_ireq_tuser    ),//用户发送数据端口的数据请求的AXI的源ID和目的ID信号;
        .m_axis_iresp_tvalid    ( m0_axis_iresp_tvalid  ),//用户发送数据端口的数据响应的AXI数据有效指示信号;
        .m_axis_iresp_tready    ( m0_axis_iresp_tready  ),//用户发送数据端口的数据响应的AXI数据应答信号;
        .m_axis_iresp_tlast     ( m0_axis_iresp_tlast   ),//用户发送数据端口的数据响应的AXI最后一个数据有效指示信号;
        .m_axis_iresp_tdata     ( m0_axis_iresp_tdata   ),//用户发送数据端口的数据响应的AXI数据信号;
        .m_axis_iresp_tkeep     ( m0_axis_iresp_tkeep   ),//用户发送数据端口的数据响应的AXI数据掩码信号;
        .m_axis_iresp_tuser     ( m0_axis_iresp_tuser   ),//用户发送数据端口的数据响应的AXI的源ID和目的ID信号;
        .m_axis_treq_tvalid     ( m0_axis_treq_tvalid   ),//用户接收数据端口的数据请求的AXI数据有效指示信号;
        .m_axis_treq_tready     ( m0_axis_treq_tready   ),//用户接收数据端口的数据请求的AXI数据应答信号;
        .m_axis_treq_tlast      ( m0_axis_treq_tlast    ),//用户接收数据端口的数据请求的AXI最后一个数据有效指示信号;
        .m_axis_treq_tdata      ( m0_axis_treq_tdata    ),//用户接收数据端口的数据请求的AXI数据信号;
        .m_axis_treq_tkeep      ( m0_axis_treq_tkeep    ),//用户接收数据端口的数据请求的AXI数据掩码信号;
        .m_axis_treq_tuser      ( m0_axis_treq_tuser    ),//用户接收数据端口的数据请求的AXI的源ID和目的ID信号;
        .s_axis_tresp_tvalid    ( s0_axis_tresp_tvalid  ),//用户接收数据端口的数据响应的AXI数据有效指示信号;
        .s_axis_tresp_tready    ( s0_axis_tresp_tready  ),//用户接收数据端口的数据响应的AXI数据应答信号;
        .s_axis_tresp_tlast     ( s0_axis_tresp_tlast   ),//用户接收数据端口的数据响应的AXI最后一个数据有效指示信号;
        .s_axis_tresp_tdata     ( s0_axis_tresp_tdata   ),//用户接收数据端口的数据响应的AXI数据信号;
        .s_axis_tresp_tkeep     ( s0_axis_tresp_tkeep   ),//用户接收数据端口的数据响应的AXI数据掩码信号;
        .s_axis_tresp_tuser     ( s0_axis_tresp_tuser   ),//用户接收数据端口的数据响应的AXI的源ID和目的ID信号;
        .s_axi_maintr_rst       ( 1'b0                  ),//维护端口的axi_lite复位信号;
        .s_axi_maintr_awvalid   ( 1'b0                  ),//维护端口的axi_lite写地址有效指示信号;
        .s_axi_maintr_awready   (                       ),//维护端口的axi_lite写地址应答信号;
        .s_axi_maintr_awaddr    ( 'd0                   ),//维护端口的axi_lite写地址信号;
        .s_axi_maintr_wvalid    ( 1'b0                  ),//维护端口的axi_lite写数据有效指示信号;
        .s_axi_maintr_wready    (                       ),//维护端口的axi_lite写数据应答信号;
        .s_axi_maintr_wdata     ( 'd0                   ),//维护端口的axi_lite写数据信号;
        .s_axi_maintr_bvalid    (                       ),//维护端口的axi_lite写数据应答指示信号;
        .s_axi_maintr_bready    ( 1'b0                  ),//维护端口的axi_lite写数据应答通道的应答信号;
        .s_axi_maintr_bresp     (                       ),//维护端口的axi_lite写数据的应答状态信号;
        .s_axi_maintr_arvalid   ( 1'b0                  ),//维护端口的axi_lite读地址有效指示信号;
        .s_axi_maintr_arready   (                       ),//维护端口的axi_lite读地址应答信号;
        .s_axi_maintr_araddr    ( 'd0                   ),//维护端口的axi_lite读地址信号;
        .s_axi_maintr_rvalid    (                       ),//维护端口的axi_lite读数据有效指示信号;
        .s_axi_maintr_rready    ( 1'b0                  ),//维护端口的axi_lite读数据应答信号;
        .s_axi_maintr_rdata     (                       ),//维护端口的axi_lite读数据信号;
        .s_axi_maintr_rresp     (                       ),//维护端口的axi_lite读数据应答状态信号;
        .gt_qpll_clk            ( gt_qpll_clk           ),//QPLL的时钟信号。
        .gt_qpll_out_refclk     ( gt_qpll_out_refclk    ),//QPLL的参考时钟信号。
        .gt_qpll_lock           ( gt_qpll_lock          ),//QPLL的时钟锁定信号。
        .gt_qpll_rst            ( gt_qpll_rst           ),//QPLL复位信号;
        .gt_qplllockdetclk      ( gt_qplllockdetclk     ),
        .sim_train_en           ( TRAIN_EN              ),//高电平用于加快仿真速度。
        .port_error             (                       ),//表示端口收到不可恢复的错误,并处于错误状态。
        .port_initialized       ( port_initialized_0    ),//表示端口已经初始化。
        .gtrx_disperr           (                       ),//高电平表示字节对齐错误。
        .gtrx_notintable        (                       ),//高电平表示8B/10B解码错误。
        .srio_tx_n              ( srio_tx_0_n           ),//SRIO的发送数据差分信号;
        .srio_tx_p              ( srio_tx_0_p           ),//SRIO的发送数据差分信号;
        .srio_rx_n              ( srio_rx_0_n           ),//SRIO的接收数据差分信号;
        .srio_rx_p              ( srio_rx_0_p           ) //SRIO的接收数据差分信号;
    );

    //例化通道1的SRIO IP
    srio_channel u_srio_channel_1(
        .gt_refclk              ( gt_refclk             ),//参考时钟信号;
        .sys_rst                ( sys_rst               ),//系统复位信号;
        .user_clk               ( user_clk_1            ),//生成用户时钟信号;
        .user_rst               ( user_rst_1            ),//生成用户复位信号;
        .deviceid               (                       ),//存储在基本设备ID CSR中的当前值(偏移量0x60)。
        .port_decode_error      (                       ),//高电平表示收到了不支持的数据包,当接收到支持的数据包时拉低
        .s_axis_ireq_tvalid     ( s1_axis_ireq_tvalid   ),//用户发送数据端口的数据请求的AXI数据有效指示信号;
        .s_axis_ireq_tready     ( s1_axis_ireq_tready   ),//用户发送数据端口的数据请求的AXI数据应答信号;
        .s_axis_ireq_tlast      ( s1_axis_ireq_tlast    ),//用户发送数据端口的数据请求的AXI最后一个数据有效指示信号;
        .s_axis_ireq_tdata      ( s1_axis_ireq_tdata    ),//用户发送数据端口的数据请求的AXI数据信号;
        .s_axis_ireq_tkeep      ( s1_axis_ireq_tkeep    ),//用户发送数据端口的数据请求的AXI数据掩码信号;
        .s_axis_ireq_tuser      ( s1_axis_ireq_tuser    ),//用户发送数据端口的数据请求的AXI的源ID和目的ID信号;
        .m_axis_iresp_tvalid    ( m1_axis_iresp_tvalid  ),//用户发送数据端口的数据响应的AXI数据有效指示信号;
        .m_axis_iresp_tready    ( m1_axis_iresp_tready  ),//用户发送数据端口的数据响应的AXI数据应答信号;
        .m_axis_iresp_tlast     ( m1_axis_iresp_tlast   ),//用户发送数据端口的数据响应的AXI最后一个数据有效指示信号;
        .m_axis_iresp_tdata     ( m1_axis_iresp_tdata   ),//用户发送数据端口的数据响应的AXI数据信号;
        .m_axis_iresp_tkeep     ( m1_axis_iresp_tkeep   ),//用户发送数据端口的数据响应的AXI数据掩码信号;
        .m_axis_iresp_tuser     ( m1_axis_iresp_tuser   ),//用户发送数据端口的数据响应的AXI的源ID和目的ID信号;
        .m_axis_treq_tvalid     ( m1_axis_treq_tvalid   ),//用户接收数据端口的数据请求的AXI数据有效指示信号;
        .m_axis_treq_tready     ( m1_axis_treq_tready   ),//用户接收数据端口的数据请求的AXI数据应答信号;
        .m_axis_treq_tlast      ( m1_axis_treq_tlast    ),//用户接收数据端口的数据请求的AXI最后一个数据有效指示信号;
        .m_axis_treq_tdata      ( m1_axis_treq_tdata    ),//用户接收数据端口的数据请求的AXI数据信号;
        .m_axis_treq_tkeep      ( m1_axis_treq_tkeep    ),//用户接收数据端口的数据请求的AXI数据掩码信号;
        .m_axis_treq_tuser      ( m1_axis_treq_tuser    ),//用户接收数据端口的数据请求的AXI的源ID和目的ID信号;
        .s_axis_tresp_tvalid    ( s1_axis_tresp_tvalid  ),//用户接收数据端口的数据响应的AXI数据有效指示信号;
        .s_axis_tresp_tready    ( s1_axis_tresp_tready  ),//用户接收数据端口的数据响应的AXI数据应答信号;
        .s_axis_tresp_tlast     ( s1_axis_tresp_tlast   ),//用户接收数据端口的数据响应的AXI最后一个数据有效指示信号;
        .s_axis_tresp_tdata     ( s1_axis_tresp_tdata   ),//用户接收数据端口的数据响应的AXI数据信号;
        .s_axis_tresp_tkeep     ( s1_axis_tresp_tkeep   ),//用户接收数据端口的数据响应的AXI数据掩码信号;
        .s_axis_tresp_tuser     ( s1_axis_tresp_tuser   ),//用户接收数据端口的数据响应的AXI的源ID和目的ID信号;
        .s_axi_maintr_rst       ( 1'b0                  ),//维护端口的axi_lite复位信号;
        .s_axi_maintr_awvalid   ( 1'b0                  ),//维护端口的axi_lite写地址有效指示信号;
        .s_axi_maintr_awready   (                       ),//维护端口的axi_lite写地址应答信号;
        .s_axi_maintr_awaddr    ( 'd0                   ),//维护端口的axi_lite写地址信号;
        .s_axi_maintr_wvalid    ( 1'b0                  ),//维护端口的axi_lite写数据有效指示信号;
        .s_axi_maintr_wready    (                       ),//维护端口的axi_lite写数据应答信号;
        .s_axi_maintr_wdata     ( 'd0                   ),//维护端口的axi_lite写数据信号;
        .s_axi_maintr_bvalid    (                       ),//维护端口的axi_lite写数据应答指示信号;
        .s_axi_maintr_bready    ( 1'b0                  ),//维护端口的axi_lite写数据应答通道的应答信号;
        .s_axi_maintr_bresp     (                       ),//维护端口的axi_lite写数据的应答状态信号;
        .s_axi_maintr_arvalid   ( 1'b0                  ),//维护端口的axi_lite读地址有效指示信号;
        .s_axi_maintr_arready   (                       ),//维护端口的axi_lite读地址应答信号;
        .s_axi_maintr_araddr    ( 'd0                   ),//维护端口的axi_lite读地址信号;
        .s_axi_maintr_rvalid    (                       ),//维护端口的axi_lite读数据有效指示信号;
        .s_axi_maintr_rready    ( 1'b0                  ),//维护端口的axi_lite读数据应答信号;
        .s_axi_maintr_rdata     (                       ),//维护端口的axi_lite读数据信号;
        .s_axi_maintr_rresp     (                       ),//维护端口的axi_lite读数据应答状态信号;
        .gt_qpll_clk            ( gt_qpll_clk           ),//QPLL的时钟信号。
        .gt_qpll_out_refclk     ( gt_qpll_out_refclk    ),//QPLL的参考时钟信号。
        .gt_qpll_lock           ( gt_qpll_lock          ),//QPLL的时钟锁定信号。
        .gt_qpll_rst            (                       ),//QPLL复位信号;
        .gt_qplllockdetclk      (                       ),
        .sim_train_en           ( TRAIN_EN              ),//高电平用于加快仿真速度。
        .port_error             (                       ),//表示端口收到不可恢复的错误,并处于错误状态。
        .port_initialized       ( port_initialized_1    ),//表示端口已经初始化。
        .gtrx_disperr           (                       ),//高电平表示字节对齐错误。
        .gtrx_notintable        (                       ),//高电平表示8B/10B解码错误。
        .srio_tx_n              ( srio_tx_1_n           ),//SRIO的发送数据差分信号;
        .srio_tx_p              ( srio_tx_1_p           ),//SRIO的发送数据差分信号;
        .srio_rx_n              ( srio_rx_1_n           ),//SRIO的接收数据差分信号;
        .srio_rx_p              ( srio_rx_1_p           ) //SRIO的接收数据差分信号;
    );

3、测试数据生成模块

  该模块用于生成SRIO IP的测试数据,主要用于产生ireq通道的数据,并且对来自treq通道的读请求做出响应。Ireq主要产生写数据、门铃、读数据、消息等4四个事务。

  下面是控制ireq通道产生上述几个事务状态机的状态转换图。首先处于空闲状态一段时间后,通过ireq通道写入一帧数据,然后跳转到发送门铃消息状态,之后读取一帧数据,最后发送一个消息包,之后循环跳转。

在这里插入图片描述

图13 状态转换图

  该模块的端口信号列表如下所示,包含ireq、iresp、treq、tresp等四个通道。

module srio_gen_data #(
    parameter       DATA_LEN    =   5'd16            //读写一帧数据的长度,单位8字节;
)(
    input						clk		            ,//系统时钟信号;
    input						rst  	            ,//系统复位信号,高电平有效;

    input                       m_axis_ireq_tready  ,//
    output  reg                 m_axis_ireq_tvalid  ,//
    output  reg                 m_axis_ireq_tlast   ,//
    output  reg [63 : 0]        m_axis_ireq_tdata   ,//
    output  reg [7  : 0]        m_axis_ireq_tkeep   ,//
    output  reg [31 : 0]        m_axis_ireq_tuser   ,//
    input                       s_axis_iresp_tvalid ,//
    input                       s_axis_iresp_tlast  ,//
    input       [63 : 0]        s_axis_iresp_tdata  ,//
    input       [7  : 0]        s_axis_iresp_tkeep  ,//
    input       [31 : 0]        s_axis_iresp_tuser  ,//
    output  reg                 s_axis_iresp_tready ,//
    input                       s_axis_treq_tvalid  ,//
    input                       s_axis_treq_tlast   ,//
    input       [63 : 0]        s_axis_treq_tdata   ,//
    input       [7  : 0]        s_axis_treq_tkeep   ,//
    input       [31 : 0]        s_axis_treq_tuser   ,//
    output  reg                 s_axis_treq_tready  ,//
    input                       m_axis_tresp_tready ,//
    output  reg                 m_axis_tresp_tvalid ,//
    output  reg                 m_axis_tresp_tlast  ,//
    output  reg [63 : 0]        m_axis_tresp_tdata  ,//
    output  reg [7  : 0]        m_axis_tresp_tkeep  ,//
    output  reg [31 : 0]        m_axis_tresp_tuser   //
);

  下面是状态机相关代码,包含次态与现态的跳转,以及次态的跳转,注意读数据时,需要等待数据返回之后才会跳转。

    //状态机的次态与现态的转换;
    always@(posedge clk)begin
        if(rst)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end
    
    //状态机次态的跳转;
    always@(*)begin
        case(state_c)
            IDLE : begin
                if(&st_cnt)begin//经过一段时间后发送一帧写数据;
                    state_n = WRTIE;
                end
                else begin
                    state_n = state_c;
                end
            end
            WRTIE : begin//当写完一帧数据时,跳转到发送门铃消息的状态;
                if(m_axis_ireq_tready && m_axis_ireq_tvalid && m_axis_ireq_tlast)begin
                    state_n = DB;
                end
                else begin
                    state_n = state_c;
                end
            end
            DB : begin//当发送完一帧门铃消息时,跳转到读数据的状态;
                if(m_axis_ireq_tready && m_axis_ireq_tvalid && m_axis_ireq_tlast)begin
                    state_n = READ;
                end
                else begin
                    state_n = state_c;
                end
            end
            READ : begin//当读完一帧数据时,跳转到发送消息的状态;
                if(s_axis_iresp_tready && s_axis_iresp_tvalid && s_axis_iresp_tlast)begin
                    state_n = MESSAGE;
                end
                else begin
                    state_n = state_c;
                end
            end
            MESSAGE : begin//当发送完一帧消息后,跳转到空闲状态;
                if(m_axis_ireq_tready && m_axis_ireq_tvalid && m_axis_ireq_tlast)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
            default : begin
                state_n = IDLE;
            end
        endcase
    end

  下面是一个状态计数器,对状态机处于各个状态西发送或者接收数据的时钟进行计数,状态机跳转时清零。

    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            st_cnt <= 6'd0;
        end//状态跳转时计数器清零;
        else if(state_c != state_n)begin
            st_cnt <= 6'd0;
        end//当数据发送完或者处于空闲状态时加1.
        else if((m_axis_ireq_tready && m_axis_ireq_tvalid) || (s_axis_iresp_tready && s_axis_iresp_tvalid) || (state_c == IDLE))begin
            st_cnt <= st_cnt + 6'd1;
        end
    end

  由于每次发送的数据都是8字节的整数倍,因此数据掩码信号全部设为无效即可,从机接口的应答信号一直拉高,处于接收数据状态。

    //两个通道的应答信号一直有效;
    always@(posedge clk)begin
        s_axis_treq_tready <= 1'b1;
        s_axis_iresp_tready <= 1'b1;
        m_axis_ireq_tkeep <= 8'hff;//数据掩码信号;
        m_axis_ireq_tuser <= 32'd0;//发送数据帧的目的ID和源ID;
        m_axis_tresp_tuser <= 32'd0;//发送数据帧的目的ID和源ID;
        m_axis_tresp_tkeep <= 8'hff;//数据掩码信号;
        state_c_r <= state_c;
    end

  下面是ireq通道的数据有效指示信号,初始为低电平,当状态机跳转到另一个状态时拉高,当发送完一帧数据时拉低。

    //生成数据有效指示信号;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axis_ireq_tvalid <= 1'b0;
        end//当发送完一帧数据时拉低;
        else if(m_axis_ireq_tvalid && m_axis_ireq_tready && m_axis_ireq_tlast)begin
            m_axis_ireq_tvalid <= 1'b0;
        end
        else if(st_cnt == 0)begin//当状态机需要写数据、发送门铃、读数据、发送消息时拉高。
            m_axis_ireq_tvalid <= ((state_c == WRTIE) || (state_c == DB) || (state_c == READ) || (state_c == MESSAGE));
        end
    end

  Ireq通道发送最后一个数据的有效指示信号,当发送最后一个数据时拉高,其余时间拉低。

    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axis_ireq_tlast <= 1'b0;
        end//当发送完一帧数据时拉低;
        else if(m_axis_ireq_tvalid && m_axis_ireq_tready && m_axis_ireq_tlast)begin
            m_axis_ireq_tlast <= 1'b0;
        end
        else if((state_c == WRTIE) && (st_cnt == DATA_LEN - 1))begin
            m_axis_ireq_tlast <= 1'b1;
        end
        else if((state_c == DB || state_c == READ) && (st_cnt == 0))begin
            m_axis_ireq_tlast <= 1'b1;
        end
        else if((state_c == MESSAGE) && (st_cnt == 7))begin
            m_axis_ireq_tlast <= 1'b1;
        end
    end

  生成ireq通道的数据,状态机跳转时生成帧头数据,其余时间生成连续的数据。

    //生成发送数据;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axis_ireq_tdata <= 'd0;
        end
        else if(state_c != state_c_r)begin
            case (state_c)//发送帧头数据;
                WRTIE : m_axis_ireq_tdata <= {8'd0,4'b0101,4'b0100,1'b0,2'b1,1'b0,{{DATA_LEN[4:0],3'd0} - 8'd1},2'b0,34'd0};
                DB    : m_axis_ireq_tdata <= {8'd0,4'b1010,4'd0,1'b0,2'b0,1'b0,8'd0,2'b0,34'd0};
                READ  : m_axis_ireq_tdata <= {8'd0,4'b0010,4'd4,1'b0,2'b0,1'b0,{{DATA_LEN[4:0],3'd0} - 8'd1},2'b0,34'd0};
                MESSAGE:m_axis_ireq_tdata <= {4'd0,4'd0,4'b1011,4'd0,1'b0,2'b0,1'b0,8'd63,2'b0,34'd0};
                default : ;
            endcase
        end//其余时间将计数器数据拼接作为测试数据;
        else if(m_axis_ireq_tvalid && m_axis_ireq_tready)begin
            m_axis_ireq_tdata <= {{9'd0,st_cnt[4:0],2'd0},{9'd0,st_cnt[4:0],2'd1},{9'd0,st_cnt[4:0],2'd2},{9'd0,st_cnt[4:0],2'd3}};
        end
    end

  下面是响应读命令的相关代码,rx_flag为高电平时,表示接收一帧treq数据,然后检测treq的读请求指令,并且解析出需要读取数据的长度,单位8个字节。

    //检测接收的读命令;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            rx_flag <= 1'b0;
        end
        else if(s_axis_treq_tvalid && s_axis_treq_tready)begin
            if(s_axis_treq_tlast)//检测到一帧数据的最后一个数据时拉低;
                rx_flag <= 1'b0;
            else
                rx_flag <= 1'b1;
        end
    end

    //检测到读指令。
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            read_cmd <= 1'b0;
            read_data_len <= 'd31;
        end//当接收到读指令时将读命令拉高;
        else if(s_axis_treq_tvalid && s_axis_treq_tready && (~rx_flag))begin
            read_cmd <= (s_axis_treq_tdata[55:48] == 8'h24);
            read_data_len <= s_axis_treq_tdata[43:36] ? ((s_axis_treq_tdata[43:36] + 1) >> 3) : DATA_LEN;//将读数据长度暂存;
        end
        else begin//其余时间拉低;
            read_cmd <= 1'b0;
        end
    end

  然后生成读响应(tresp通道)的数据有效指示信号和最后一个数据有效指示信号。

    //发送带数据的响应报文;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axis_tresp_tvalid <= 1'b0;
        end//当发送完一帧数据时拉低;
        else if(m_axis_tresp_tvalid && m_axis_tresp_tready && m_axis_tresp_tlast)begin
            m_axis_tresp_tvalid <= 1'b0;
        end//当检测到读命令时拉高,开始发送读响应报文;
        else if(read_cmd)begin
            m_axis_tresp_tvalid <= 1'b1;
        end
    end

    //生成一帧数据的最后一个数据有效指示信号;
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            m_axis_tresp_tlast <= 1'b0;
        end
        else if(m_axis_tresp_tvalid && m_axis_tresp_tready && m_axis_tresp_tlast)begin
            m_axis_tresp_tlast <= 1'b0;//读完数据时拉低;
        end
        else if(read_cnt[4:0] == read_data_len - 1)begin
            m_axis_tresp_tlast <= 1'b1;//读出最后一个数据时拉高;
        end
    end

  最后时生成读数据,为了方便测试,读请求的返回数据使用一组递增的数据替代,包头使用带返回数据的应答包。

  由于本文篇幅比较长了,下文仿真SRIO工程,并且通过光纤回环上板测试。


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

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

相关文章:

  • MapReduce简单应用(三)——高级WordCount
  • Datawhale Ollama教程笔记3
  • excel中单元格字符串提取数字累加
  • Linux部署DeepSeek r1 模型训练
  • Python+appium实现自动化测试
  • Netty的线程模型详解
  • ollama+langchain+deepseek本机跑通大模型
  • spring 学习(spring-Dl补充(注入不同类型的数据))
  • 搭建本地模型,实现聊天机器人
  • 二级C语言题解:函数指针的操作、单链表偶数结点值累加、判断回文
  • 2025年——【寒假】自学黑客计划(网络安全)
  • 达梦:TPCC 压测
  • 解决Did not find dashscope_api_key问题——jupyter设置环境变量
  • 初学总结SpringBoot项目在mac上环境搭建和运行
  • 教育小程序+AI出题:如何通过自然语言处理技术提升题目质量
  • [npm install 报错] Verion 9 of Highlight.js has reached EOL
  • ESP32 WIFI
  • 封装一个sqlite3动态库
  • C#两个集合多属性组合关联得到新的组合
  • 机器学习算法 - 随机森林之决策树初探(1)
  • 王毅同巴基斯坦副总理兼外长达尔通电话
  • 广西钦州:坚决拥护自治区党委对钟恒钦进行审查调查的决定
  • 告别户口本!今天起婚姻登记实现全国通办
  • 人民日报评“组团退演出服”:市场经济诚信原则需全社会维护
  • 现场丨“影像上海”启幕:串联摄影、电影与当代艺术
  • 国家发改委:目前有的核电项目民间资本参股比例已经达到20%