查看: 2467|回复: 0

[讨论] 什么是Xilinx的PCIe仿真模型?

[复制链接]
  • TA的每日心情
    无聊
    2018-10-12 09:46
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2019-8-16 10:44:03 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 qazwsx01 于 2019-8-16 10:46 编辑

    什么是Xilinx公司的的PCIe仿真模型?
    这里所说的Xilinx PCIe仿真模型是指在例化PCIe核时候其自动生成的参考时自动产生的仿真模型,用户可以用此模型来仿真其PCIe设计。这里着重学习为仿真端点提供的RP模型, Xilinx称其为下游端口模型,简称DS端口模型。该模型框架图见下图。

    图中的dsport模块,个人理解是模拟根端,而dsport上方的usrapp_rx和usrapp_tx可以理解为驱动+用户层程序,所以学习这2个usrapp源文件,对于用户理解PCIE的驱动和顶层应用程序开发还是有好处的。该模型由于是下游端口,所以大部分测试功能都在usrapp_tx里实现并发起。所以后面学习的重点是在仿真的同时,理解吃透usrapp_tx及其从它出调用的子程序。

    使用Xilinx的PCIe的时候,例化示例都会自动生成仿真模型,同时官网提供的基于xapp1052的参考设计,也同时提供仿真BMD的仿真模型。即所谓的下行端口模型(下游端口模型),所以PCIe例化之后产生的参考设计都会自动生成一个dsport的模型文件,让用户可以以此模拟一个根端口来测试其端点端口下图给出了下行端口模型的结构框图。:

    1.jpg
    图1:DS端口模型结构框图

    当然,上图是仿真端点的模型,而且大部分用户都是实例化端点,如果是设计根端口的话,可以忽略本文。以下描述都是基于使用端点。用户例化的的PCIe核可是视为EP,上图DS模型就是RP,仿真顶层还需要一个测试台将RP和EP包裹起来,所有这些文件在的PCIe核例化的时候自动产生,具体位置(这里以xapp1052为例)为:

    2.jpg
    图2:仿真文件存放位置

    如图2所示,dsport文件夹存放都是图1灰色框部分文件,功能文件夹存放的是测试平台顶层以及系统时钟和复位生成文件,而测试文件夹则存放了用户层测试激励(体现与图1右侧的“测试程序”),这些激励都是usrapp_tx发起。下面会试着详细介绍图1中的几个灰框里的文件,主要是usr_app_rx和usr_app_tx以及测试程序。

    dsport(下游端口)

    这个模块主要实现root complex,Xilinx一直强调这个模块不能看成是严格的root complex,因为它并不能提供真正root complex提供的很多特性,只是方便用户仿真而创建的模型。用户侧的端点经PCIe链路发送TLP到下行端口(下游端口)模型。

    xapp1052里实现dsport的源文件主要应该是xilinx_pcie_2_1_rport_7x.v,pcie_2_1_rport_7x.v,而文件pci_exp_usrapp_cfg.v用来对DS模型进行配置。

    RX_APP(pci_exp_usrapp_rx.v)

    在RX_APP可能主要定义了解析接收用户接口状态机该状态机的状态变量如下:
        / *状态变量* /`定义TRN_RX_RESET 5'b00001`define TRN_RX_DOWN 5'b00010`define TRN_RX_IDLE 5'b00100`define TRN_RX_ACTIVE 5'b01000`define TRN_RX_SRC_DSC 5'b10000

    TX_APP(pci_exp_usrapp_tx.v)

    usrapp_tx为的PCIe链路两端的数据传输,发送TLP到dsport模块。包解析或测试程序由usrapp_tx启动,用于仿真终端接口。所有测试程序都是实现定义好的,存放在上述测试文件夹下的文件之中(tests.vh)。在usrapp_tx源文件中直接调用tests.vh


        //测试从这里开始如果(testname ==“dummy_test”)开始$ display(“[%t]%m:无效的TESTNAME:%0s”,$ realtime,testname); $光洁度(2); 结束`include“tests.vh”否则开始$ display(“[%t]%m:错误:无法识别的TESTNAME:%0s”,$ realtime,testname); $光洁度(2); 结束
    而在tests.vh文件中则通过包括语句将存放测试程序的源文件包括进来,赛灵思提供很多事先定义好的测试程序,但是在xapp1052示例中的sample_tests1.vh仅提供了三个测试程序,它们分别是sample_smoke_test0,sample_smoke_test1以及pio_writeReadBack_test0。

    sample_smoke_test0发起一个PCI Type0配置读TLP,并等待完成TLP;然后将返回值与预期的器件ID和供应商ID进行比较。

    sample_smoke_test1执行和sample_smoke_test0一样的操作,但是使用了可预期任务程序。这个测试使用了两个独立的测试程序线程:一个线程发送PCI Type0配置读TLP,第二个线程提交一个带数据的完成报文TLP可预期任务。这个测试展示了使用可预期任务实现并行测试的结构。该测试可以用于确认从用户设计收到的任何TLP,也可以在当顺序不重要时确认收到的TLP。

    pio_writeReadBack_test0测试程序先发送一个单DW写TLP,然后发送一个单DW读TLP;然后等待完成报文TLP,并读写验证数据是否harmony和谐


    不管测试程序要实现怎样的功能,都大致分为以下6个步骤:

    • 执行条件比较特定的测试名称(比如确认当前测试是不是pio_writeReadBack_test0?或其它测试)
    • 设置仿真退出时间,防止仿真进挂起
    • 等待正常复位,以及链路链接(链接时)
    • 初始化端点配置空间
    • 在DS端口模型和终端直接发送和接收TLP
    • 验证测试是否成功


    pio_writeReadBack_test0


    本节来看看pio_writeReadBack_test0测试程序里的具体代码
    else if(testname ==“pio_writeReadBack_test0”)start //此测试执行32位写入32位存储空间并执行读回// board.RP.tx_usrapp.TSK_SIMULATION_TIMEOUT(10050); board.RP.tx_usrapp。 TSK_SIMULATION_TIMEOUT(20050); board.RP.tx_usrapp.TSK_SYSTEM_INITIALIZATION; board.RP.tx_usrapp.TSK_BAR_INIT; // --------------------------- ----------------------------------------------- //活动:测试BAR // --------------------------------------------- ----------------------------- for(board.RP.tx_usrapp.ii = 0; board.RP.tx_usrapp.ii <= 6; board.RP.tx_usrapp.ii = board.RP.tx_usrapp.ii + 1)beginif(board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED [board.RP.tx_usrapp.ii]> 2'b00)// bar是enabledcase(board .RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED [board.RP.tx_usrapp.ii])2'b01:// IO SPACEbegin $ display(“[%t]:将TLP传输到IO空间BAR%x“,$ realtime,board.RP.tx_usrapp.ii); // -------------------------- ------------------------------------------------ //事件:IO写入位TLP // ------------------------------------------ -------------------------------- board.RP.tx_usrapp.TSK_TX_IO_WRITE(board.RP.tx_usrapp.DEFAULT_TAG,板。 RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0],4'hF,32'hdead_beef); board.RP.com_usrapp.TSK_EXPECT_CPL(3'h0,1'b0,1'b0,2 'b0,board.RP.tx_usrapp.COMPLETER_ID_CFG,3'h0,1'b0,12'h4,board.RP.tx_usrapp.COMPLETER_ID_CFG,board.RP.tx_usrapp.DEFAULT_TAG,board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP .tx_usrapp.ii] [31:0],test_vars [0]); board.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;// ------------------------------------------------ -------------------------- //事件:IO读取位TLP // -------------- -------------------------------------------------- ---------- //确保P_READ_DATA已知初始值value.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff; forkboard.RP.tx_usrapp.TSK_TX_IO_READ(board.RP.tx_usrapp.DEFAULT_TAG,board.RP。 tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0],4'hF); board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA; joinif(board.RP.tx_usrapp.P_READ_DATA!= 32'hdead_beef)开始$ display( “[%t]:测试失败---数据错误不匹配,写入数据%x!=读取数据%x”,$ realtime,32'hdead_beef,board.RP.tx_usrapp.P_READ_DATA); test_failed_flag = 1; endelsebegin $ display (“[%t]:测试通过---写入数据:%x成功接收”,$ realtime,board.RP.tx_usrapp.P_READ_DATA); endboard.RP。tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; end2'b10:// MEM 32 SPACEbegin $ display(“[%t]:将TLP传输到内存32空间BAR %x“,$ realtime,board.RP.tx_usrapp.ii); // -------------------------------- ------------------------------------------ //事件:内存写32位TLP // ----------------------------------------------- --------------------------- board.RP.tx_usrapp.DATA_STORE [0] = 8'h04; board.RP.tx_usrapp.DATA_STORE [ 1] = 8'h03; board.RP.tx_usrapp.DATA_STORE [2] = 8'h02; board.RP.tx_usrapp.DATA_STORE [3] = 8'h01; board.RP.tx_usrapp.TSK_TX_MEMORY_WRITE_32(board.RP.tx_usrapp .DEFAULT_TAG,board.RP.tx_usrapp.DEFAULT_TC,10'd1,// board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0] + 8'h10,4'h0,4'hF ,1'b0); //由Jerryboard.RP修改。tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0] + 8'h08,4'h0,4'hF,1'b0); board.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP。 tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; // ---------------------------------- ---------------------------------------- //事件:内存读取32位TLP / / ------------------------------------------------- ------------------------- //确保P_READ_DATA已知初始值value.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff; forkboard.RP.tx_usrapp .TSK_TX_MEMORY_READ_32(board.RP.tx_usrapp.DEFAULT_TAG,board.RP.tx_usrapp.DEFAULT_TC,10'd1,// board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0] + 8'h10 ,4'h0,4'hF); //由Jerryboard.RP.tx_usrapp.BAR_INIT_P_BAR修改[board.RP.tx_usrapp.ii] [31:0] + 8'h08,4'h0,4'hF);板.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;joinif(board.RP.tx_usrapp.P_READ_DATA!= {board.RP.tx_usrapp.DATA_STORE [3],board.RP.tx_usrapp.DATA_STORE [2],board.RP.tx_usrapp.DATA_STORE [1],board.RP.tx_usrapp .DATA_STORE [0]})开始$ display(“[%t]:测试失败---数据错误不匹配,写入数据%x!=读取数据%x”,$ realtime,{board.RP.tx_usrapp.DATA_STORE [ 3],board.RP.tx_usrapp.DATA_STORE [2],board.RP.tx_usrapp.DATA_STORE [1],board.RP.tx_usrapp.DATA_STORE [0]},board.RP.tx_usrapp.P_READ_DATA); test_failed_flag = 1; endelsebegin $ display(“[%t]:Test PASSED ---写入数据:%x成功收到”,$ realtime,board.RP.tx_usrapp.P_READ_DATA); endboard.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP .tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; end2'b11:// MEM 64 SPACEbegin $ display(“[%t]:将TLP传输到内存64空间BAR%x”,$ realtime,board.RP .tx_usrapp。ⅱ); // --------------------------------------------- ----------------------------- //事件:内存写64位TLP // ---------- -------------------------------------------------- -------------- board.RP.tx_usrapp.DATA_STORE [0] = 8'h64; board.RP.tx_usrapp.DATA_STORE [1] = 8'h63; board.RP.tx_usrapp。 DATA_STORE [2] = 8'h62; board.RP.tx_usrapp.DATA_STORE [3] = 8'h61; board.RP.tx_usrapp.TSK_TX_MEMORY_WRITE_64(board.RP.tx_usrapp.DEFAULT_TAG,board.RP.tx_usrapp.DEFAULT_TC,10' D1,{board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii + 1] [31:0],board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0] 8 'h20},4'h0,4'hF,1'b0); board.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; //  - -------------------------------------------------- ---------------------- //活动:内存读取64位TLP // ------------------------------------------- ------------------------------- //确保P_READ_DATA已知初始值value.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff ; forkboard.RP.tx_usrapp.TSK_TX_MEMORY_READ_64(board.RP.tx_usrapp.DEFAULT_TAG,board.RP.tx_usrapp.DEFAULT_TC,10'd1,{board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii + 1] [31 :0],board.RP.tx_usrapp.BAR_INIT_P_BAR [board.RP.tx_usrapp.ii] [31:0] + 8'h20},4'h0,4'hF); board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA; joinif( board.RP.tx_usrapp.P_READ_DATA!= {board.RP.tx_usrapp.DATA_STORE [3],board.RP.tx_usrapp.DATA_STORE [2],board.RP.tx_usrapp.DATA_STORE [1],board.RP.tx_usrapp.DATA_STORE [0]})开始$ display(“[%t]:测试失败---数据错误不匹配,写入数据%x!=读取数据%x”,$ realtime,{board.RP.tx_usrapp.DATA_STORE [3] ,board.RP.tx_usrapp。DATA_STORE [2],board.RP.tx_usrapp.DATA_STORE [1],board.RP.tx_usrapp.DATA_STORE [0]},board.RP.tx_usrapp.P_READ_DATA); test_failed_flag = 1; endelsebegin $ display(“[%t] :Test PASSED ---写入数据:%x成功收到“,$ realtime,board.RP.tx_usrapp.P_READ_DATA); endboard.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP。 tx_usrapp.DEFAULT_TAG + 1; enddefault:$ display(“usrapp_tx \ n”中的错误情况); endcaseend $ display(“[%t]:PCI-Express TLP的完成传输”,$ realtime); if(!test_failed_flag)开始$ display(“测试成功完成”);结束$ finish;结束$ realtime,board.RP.tx_usrapp.P_READ_DATA); endboard.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; enddefault:$ display(“error case in usrapp_tx \ n“); endcaseend $ display(”[%t]:完成PCI-Express TLP传输“,$ realtime); if(!test_failed_flag)开始$ display(”测试成功完成“);结束$ finish;结束$ realtime,board.RP.tx_usrapp.P_READ_DATA); endboard.RP.tx_usrapp.TSK_TX_CLK_EAT(10); board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1; enddefault:$ display(“error case in usrapp_tx \ n“); endcaseend $ display(”[%t]:完成PCI-Express TLP传输“,$ realtime); if(!test_failed_flag)开始$ display(”测试成功完成“);结束$ finish;结束
    board.RP.tx_usrapp.TSK_SIMULATION_TIMEOUT(10050);

    这一句设置了仿真推出的时间

    board.RP.tx_usrapp.TSK_SYSTEM_INITIALIZATION;

    。这个函数让测试程序等待系统复位被释放,同时端点的trn_lnk_up_n信号被置位这样就表示端点已经准备好通过DS端口模型被测试程序配置

    .board.RP.tx_usrapp.TSK_BAR_INIT;

    执行一系列对端点核PCI配置空间进行Type0配置写和读,确认端点的存储器和IO需求,然后对端点的基地址寄存器(巴)进行编程,这样确保可以从DS端口模型介绍TLP。

    其实的BAR空间初始化任务里同时调用了其它几个任务子程序:
        / ******************* ***********任务:TSK_BAR_INIT输入:无输出:无描述:根据内核配置初始化PCI内核。************************************************** *********** / task TSK_BAR_INIT; 开始TSK_BAR_SCAN; TSK_BUILD_PCIE_MAP; TSK_DISPLAY_PCIE_MAP; TSK_BAR_PROGRAM; end endtask // TSK_BAR_INIT 首先我们来看子程序TSK_BAR_SCAN,该子程序对6个Bar和一个扩展ROM Bar通过Type0配置读写进行配置,下面列出了BAR0的配置读写:
        //确定BAR0的范围TSK_TX_TYPE0_CONFIGURATION_WRITE(DEFAULT_TAG,12'h10,P_ADDRESS_MASK,4'hF); DEFAULT_TAG = DEFAULT_TAG + 1; TSK_TX_CLK_EAT(100); //读取BAR0范围TSK_TX_TYPE0_CONFIGURATION_READ(DEFAULT_TAG,12'h10,4'hF); DEFAULT_TAG = DEFAULT_TAG + 1; TSK_WAIT_FOR_READ_DATA; BAR_INIT_P_BAR_RANGE [0] = P_READ_DATA; 具体

    仿真结果是: 3.jpg 图3:TSK_TX_SYNCHRONIZE

    这个子程序的主要功能是同步trn_clk和trn_tdst_rdy_n信号。当一个TLP被发送之前,必须等待trn_clk的上升沿和trn_tdst_rdy_n被置位。在这个子程序之中调用了子程序TSK_READ_DATA_128和TSK_PARSE_FRAME,主要用意是输出日志信息到tx.dat文件中。

    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-11-26 15:35 , Processed in 0.134848 second(s), 18 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.