原创 Xilinx的PCIe仿真模型学习

2019-8-7 13:38 5899 25 4 分类: FPGA/CPLD 文集: PCIE
使用Xilinx的PCIe的时候,例化示例都会自动生成仿真模型,同时官网提供的基于xapp1052的参考设计,也同时提供仿真BMD的仿真模型。即所谓的下行端口模型(downstream port model),所以PCIe例化之后产生的参考设计都会自动生成一个dsport的模型文件,让用户可以以此模拟一个root端口来测试其endpoint端口。下图给出了下行端口模型的结构框图:

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

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

图2:仿真文件存放位置

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

dsport(downstream port)
这个模块主要实现root complex,Xilinx一直强调这个模块不能看成是严格的root complex,因为它并不能提供真正root complex提供的很多特性,只是方便用户仿真而创建的模型。用户侧的endpoint经PCIe链路发送TLP到下行端口(downstream port)模型。
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可能主要定义了解析接收用户接口状态机。该状态机的状态变量如下:
  1. /* State variables */
  2. `define TRN_RX_RESET 5'b00001
  3. `define TRN_RX_DOWN 5'b00010
  4. `define TRN_RX_IDLE 5'b00100
  5. `define TRN_RX_ACTIVE 5'b01000
  6. `define TRN_RX_SRC_DSC 5'b10000






TX_APP(pci_exp_usrapp_tx.v)
usrapp_tx为PCIe链路两端的数据传输,发送TLP到dsport模块。包解析或测试程序由usrapp_tx启动,用于仿真endpoint接口。所有测试程序都是实现定义好的,存放在上述tests文件夹下的文件之中(tests.vh)。在usrapp_tx源文件中直接调用tests.vh
  1. //Test starts here
  2. if (testname == "dummy_test")
  3. begin
  4. $display("[%t] %m: Invalid TESTNAME: %0s", $realtime, testname);
  5. $finish(2);
  6. end
  7. `include "tests.vh"
  8. else begin
  9. $display("[%t] %m: Error: Unrecognized TESTNAME: %0s", $realtime, testname);
  10. $finish(2);
  11. end

而在tests.vh文件中则通过include语句将存放测试程序的源文件包括进来,Xilinx提供很多事先定义好的测试程序,但是在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,并验证读写数据是否一致。


不管测试程序要实现怎样的功能,都大致分为以下6个步骤:
  1. 执行条件比较特定的测试名称(比如确认当前测试是不是pio_writeReadBack_test0?或其它测试)
  2. 设置仿真退出时间,防止仿真进挂起
  3. 等待正常复位,以及链路链接(link-up)
  4. 初始化endpoint配置空间
  5. 在DS端口模型和endpoint直接发送和接收TLP
  6. 验证测试是否成功
pio_writeReadBack_test0

本节来看看pio_writeReadBack_test0测试程序里的具体代码
  1. else if(testname == "pio_writeReadBack_test0")
  2. begin
  3. // This test performs a 32 bit write to a 32 bit Memory space and performs a read back
  4. //board.RP.tx_usrapp.TSK_SIMULATION_TIMEOUT(10050);
  5. board.RP.tx_usrapp.TSK_SIMULATION_TIMEOUT(20050);
  6. board.RP.tx_usrapp.TSK_SYSTEM_INITIALIZATION;
  7. board.RP.tx_usrapp.TSK_BAR_INIT;
  8. //--------------------------------------------------------------------------
  9. // Event : Testing BARs
  10. //--------------------------------------------------------------------------
  11. for (board.RP.tx_usrapp.ii = 0; board.RP.tx_usrapp.ii <= 6; board.RP.tx_usrapp.ii =
  12. board.RP.tx_usrapp.ii + 1) begin
  13. if (board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED[board.RP.tx_usrapp.ii] > 2'b00) // bar is enabled
  14. case(board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED[board.RP.tx_usrapp.ii])
  15. 2'b01 : // IO SPACE
  16. begin
  17. $display("[%t] : Transmitting TLPs to IO Space BAR %x", $realtime, board.RP.tx_usrapp.ii);
  18. //--------------------------------------------------------------------------
  19. // Event : IO Write bit TLP
  20. //--------------------------------------------------------------------------
  21. board.RP.tx_usrapp.TSK_TX_IO_WRITE(board.RP.tx_usrapp.DEFAULT_TAG,
  22. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0], 4'hF, 32'hdead_beef);
  23. board.RP.com_usrapp.TSK_EXPECT_CPL(3'h0, 1'b0, 1'b0, 2'b0,
  24. board.RP.tx_usrapp.COMPLETER_ID_CFG, 3'h0, 1'b0, 12'h4,
  25. board.RP.tx_usrapp.COMPLETER_ID_CFG, board.RP.tx_usrapp.DEFAULT_TAG,
  26. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0], test_vars[0]);
  27. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  28. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  29. //--------------------------------------------------------------------------
  30. // Event : IO Read bit TLP
  31. //--------------------------------------------------------------------------
  32. // make sure P_READ_DATA has known initial value
  33. board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
  34. fork
  35. board.RP.tx_usrapp.TSK_TX_IO_READ(board.RP.tx_usrapp.DEFAULT_TAG,
  36. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0], 4'hF);
  37. board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;
  38. join
  39. if (board.RP.tx_usrapp.P_READ_DATA != 32'hdead_beef)
  40. begin
  41. $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %x",
  42. $realtime, 32'hdead_beef, board.RP.tx_usrapp.P_READ_DATA);
  43. test_failed_flag = 1;
  44. end
  45. else
  46. begin
  47. $display("[%t] : Test PASSED --- Write Data: %x successfully received",
  48. $realtime, board.RP.tx_usrapp.P_READ_DATA);
  49. end
  50. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  51. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  52. end
  53. 2'b10 : // MEM 32 SPACE
  54. begin
  55. $display("[%t] : Transmitting TLPs to Memory 32 Space BAR %x", $realtime,
  56. board.RP.tx_usrapp.ii);
  57. //--------------------------------------------------------------------------
  58. // Event : Memory Write 32 bit TLP
  59. //--------------------------------------------------------------------------
  60. board.RP.tx_usrapp.DATA_STORE[0] = 8'h04;
  61. board.RP.tx_usrapp.DATA_STORE[1] = 8'h03;
  62. board.RP.tx_usrapp.DATA_STORE[2] = 8'h02;
  63. board.RP.tx_usrapp.DATA_STORE[3] = 8'h01;
  64. board.RP.tx_usrapp.TSK_TX_MEMORY_WRITE_32(board.RP.tx_usrapp.DEFAULT_TAG,
  65. board.RP.tx_usrapp.DEFAULT_TC, 10'd1,
  66. // board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h10, 4'h0, 4'hF, 1'b0);//Modified By Jerry
  67. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h08, 4'h0, 4'hF, 1'b0);
  68. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  69. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  70. //--------------------------------------------------------------------------
  71. // Event : Memory Read 32 bit TLP
  72. //--------------------------------------------------------------------------
  73. // make sure P_READ_DATA has known initial value
  74. board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
  75. fork
  76. board.RP.tx_usrapp.TSK_TX_MEMORY_READ_32(board.RP.tx_usrapp.DEFAULT_TAG,
  77. board.RP.tx_usrapp.DEFAULT_TC, 10'd1,
  78. //board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h10, 4'h0, 4'hF);//Modified by Jerry
  79. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h08, 4'h0, 4'hF);
  80. board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;
  81. join
  82. if (board.RP.tx_usrapp.P_READ_DATA != {board.RP.tx_usrapp.DATA_STORE[3],
  83. board.RP.tx_usrapp.DATA_STORE[2], board.RP.tx_usrapp.DATA_STORE[1],
  84. board.RP.tx_usrapp.DATA_STORE[0] })
  85. begin
  86. $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %x",
  87. $realtime, {board.RP.tx_usrapp.DATA_STORE[3],board.RP.tx_usrapp.DATA_STORE[2],
  88. board.RP.tx_usrapp.DATA_STORE[1],board.RP.tx_usrapp.DATA_STORE[0]},
  89. board.RP.tx_usrapp.P_READ_DATA);
  90. test_failed_flag = 1;
  91. end
  92. else
  93. begin
  94. $display("[%t] : Test PASSED --- Write Data: %x successfully received",
  95. $realtime, board.RP.tx_usrapp.P_READ_DATA);
  96. end
  97. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  98. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  99. end
  100. 2'b11 : // MEM 64 SPACE
  101. begin
  102. $display("[%t] : Transmitting TLPs to Memory 64 Space BAR %x", $realtime,
  103. board.RP.tx_usrapp.ii);
  104. //--------------------------------------------------------------------------
  105. // Event : Memory Write 64 bit TLP
  106. //--------------------------------------------------------------------------
  107. board.RP.tx_usrapp.DATA_STORE[0] = 8'h64;
  108. board.RP.tx_usrapp.DATA_STORE[1] = 8'h63;
  109. board.RP.tx_usrapp.DATA_STORE[2] = 8'h62;
  110. board.RP.tx_usrapp.DATA_STORE[3] = 8'h61;
  111. board.RP.tx_usrapp.TSK_TX_MEMORY_WRITE_64(board.RP.tx_usrapp.DEFAULT_TAG,
  112. board.RP.tx_usrapp.DEFAULT_TC, 10'd1,
  113. {board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii+1][31:0],
  114. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h20}, 4'h0, 4'hF, 1'b0);
  115. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  116. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  117. //--------------------------------------------------------------------------
  118. // Event : Memory Read 64 bit TLP
  119. //--------------------------------------------------------------------------
  120. // make sure P_READ_DATA has known initial value
  121. board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
  122. fork
  123. board.RP.tx_usrapp.TSK_TX_MEMORY_READ_64(board.RP.tx_usrapp.DEFAULT_TAG,
  124. board.RP.tx_usrapp.DEFAULT_TC, 10'd1,
  125. {board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii+1][31:0],
  126. board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h20}, 4'h0, 4'hF);
  127. board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;
  128. join
  129. if (board.RP.tx_usrapp.P_READ_DATA != {board.RP.tx_usrapp.DATA_STORE[3],
  130. board.RP.tx_usrapp.DATA_STORE[2], board.RP.tx_usrapp.DATA_STORE[1],
  131. board.RP.tx_usrapp.DATA_STORE[0] })
  132. begin
  133. $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %x",
  134. $realtime, {board.RP.tx_usrapp.DATA_STORE[3],
  135. board.RP.tx_usrapp.DATA_STORE[2], board.RP.tx_usrapp.DATA_STORE[1],
  136. board.RP.tx_usrapp.DATA_STORE[0]}, board.RP.tx_usrapp.P_READ_DATA);
  137. test_failed_flag = 1;
  138. end
  139. else
  140. begin
  141. $display("[%t] : Test PASSED --- Write Data: %x successfully received",
  142. $realtime, board.RP.tx_usrapp.P_READ_DATA);
  143. end
  144. board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
  145. board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
  146. end
  147. default : $display("Error case in usrapp_tx\n");
  148. endcase
  149. end
  150. $display("[%t] : Finished transmission of PCI-Express TLPs", $realtime);
  151. if (!test_failed_flag) begin
  152. $display ("Test Completed Successfully");
  153. end
  154. $finish;
  155. end


board.RP.tx_usrapp.TSK_SIMULATION_TIMEOUT(10050);

这一句设置了仿真推出的时间
board.RP.tx_usrapp.TSK_SYSTEM_INITIALIZATION;
这个函数让测试程序等待系统复位被释放,同时endpoint的trn_lnk_up_n信号被置位。这样就表示endpoint已经准备好通过DS端口模型被测试程序配置。
board.RP.tx_usrapp.TSK_BAR_INIT;
执行一系列对endpoint核PCI配置空间进行Type0 配置写和读,确认endpoint的存储器和IO需求,然后对endpoint的基地址寄存器(BARs)进行编程,这样确保可以从DS端口模型介绍TLP。
其实BARs空间初始化任务里同时调用了其它几个任务子程序:
  1. /************************************************************
  2. Task : TSK_BAR_INIT
  3. Inputs : None
  4. Outputs : None
  5. Description : Initialize PCI core based on core's configuration.
  6. *************************************************************/
  7. task TSK_BAR_INIT;
  8. begin
  9. TSK_BAR_SCAN;
  10. TSK_BUILD_PCIE_MAP;
  11. TSK_DISPLAY_PCIE_MAP;
  12. TSK_BAR_PROGRAM;
  13. end
  14. endtask // TSK_BAR_INIT

首先我们来看子程序TSK_BAR_SCAN,该子程序对6个Bar和一个扩展ROM Bar通过Type0配置读写进行配置,下面列出了BAR0的配置读写:
  1. // Determine Range for BAR0
  2. TSK_TX_TYPE0_CONFIGURATION_WRITE(DEFAULT_TAG, 12'h10, P_ADDRESS_MASK, 4'hF);
  3. DEFAULT_TAG = DEFAULT_TAG + 1;
  4. TSK_TX_CLK_EAT(100);
  5. // Read BAR0 Range
  6. TSK_TX_TYPE0_CONFIGURATION_READ(DEFAULT_TAG, 12'h10, 4'hF);
  7. DEFAULT_TAG = DEFAULT_TAG + 1;
  8. TSK_WAIT_FOR_READ_DATA;
  9. BAR_INIT_P_BAR_RANGE[0] = P_READ_DATA;
具体仿真结果是:

图3:

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



作者: coyoo, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-1010859.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

给作者打赏,鼓励TA抓紧创作!

赞赏支持
点赞 25
赞赏0

文章评论2条评论)

登录后参与讨论

开发工匠 2019-8-9 16:06

写的好,很有价值,参考和学习了

curton 2019-8-7 20:58

学习了
相关推荐阅读
coyoo 2024-03-13 16:48
极零相消电路测试
极零相消电路的硬件配置需要根据前端信号源输出电容来确定,根据我们的系统大致测算前端输出电容的充放电时间常数在40~50ns之间。所以最开始选用的配置是3.3nF和15欧姆。经过不管迭代检测,最终确定为...
coyoo 2024-03-08 13:15
极零相消在实际电路中的应用
极零相消在实际电路中的应用概述 极零相消也被称为极零补偿(PZC, Pole-Zero Cancellation/Compensation),最近针对PZC电路有过几轮讨论,个人也有些思考,前面也有一...
coyoo 2024-03-01 12:00
从极零相消得到的一些思考
这几天上头给出一个参考电路,并对比项目中电路进行了几轮来回讨论。数字工程师介入模拟电路的讨论、设计,而且投入实际应用,可能遇到很多挫折、打击,回头看看,对技术的理解,对系统架构的认识逐渐清晰。这是个什...
coyoo 2024-01-19 14:42
Quartus Prime是否隐藏了ECO功能?(答案已找到:仅部分高端器件支持)
QPP(Quartus Prime Pro)版本为22.4,开发中,打开Chip Planner,发现其Editing mode里的ECO - “器件型号”选项是灰色的,入下图所示:所以打开Chip ...
coyoo 2024-01-16 15:46
ALTERA CYCLONE 10器件使用 - 2
最近在讨论Cyclone 10 GX器件的使用时,有关其高速串行接口(GXB或XCVR或tranceiver)的接法,参考了ALTERA的PCI-e开发板。PCIe接口就不说了,这个板子还使用了两个S...
coyoo 2024-01-12 17:14
ALTERA Cyclone 10器件的使用 - 1
概述        在Cyclone V基础上解决了TDC实现(已在板测试通过)的问题,下一代产品应该采用新的FPGA器件。故有必要对...
EE直播间
更多
我要评论
2
25
1
2
3
4
5
6
7
8
9
0
关闭 站长推荐上一条 /6 下一条