使用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可能主要定义了解析接收用户接口状态机。该状态机的状态变量如下:
- /* State variables */
- `define 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启动,用于仿真endpoint接口。所有测试程序都是实现定义好的,存放在上述tests文件夹下的文件之中(tests.vh)。在usrapp_tx源文件中直接调用tests.vh
- //Test starts here
- if (testname == "dummy_test")
- begin
- $display("[%t] %m: Invalid TESTNAME: %0s", $realtime, testname);
- $finish(2);
- end
- `include "tests.vh"
- else begin
- $display("[%t] %m: Error: Unrecognized TESTNAME: %0s", $realtime, testname);
- $finish(2);
- 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个步骤:
- 执行条件比较特定的测试名称(比如确认当前测试是不是pio_writeReadBack_test0?或其它测试)
- 设置仿真退出时间,防止仿真进挂起
- 等待正常复位,以及链路链接(link-up)
- 初始化endpoint配置空间
- 在DS端口模型和endpoint直接发送和接收TLP
- 验证测试是否成功
pio_writeReadBack_test0本节来看看
pio_writeReadBack_test0测试程序里的具体代码- else if(testname == "pio_writeReadBack_test0")
- begin
- // This test performs a 32 bit write to a 32 bit Memory space and performs a read back
- //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;
- //--------------------------------------------------------------------------
- // Event : Testing BARs
- //--------------------------------------------------------------------------
- for (board.RP.tx_usrapp.ii = 0; board.RP.tx_usrapp.ii <= 6; board.RP.tx_usrapp.ii =
- board.RP.tx_usrapp.ii + 1) begin
- if (board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED[board.RP.tx_usrapp.ii] > 2'b00) // bar is enabled
- case(board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED[board.RP.tx_usrapp.ii])
- 2'b01 : // IO SPACE
- begin
- $display("[%t] : Transmitting TLPs to IO Space BAR %x", $realtime, board.RP.tx_usrapp.ii);
- //--------------------------------------------------------------------------
- // Event : IO Write bit TLP
- //--------------------------------------------------------------------------
- board.RP.tx_usrapp.TSK_TX_IO_WRITE(board.RP.tx_usrapp.DEFAULT_TAG,
- board.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;
- //--------------------------------------------------------------------------
- // Event : IO Read bit TLP
- //--------------------------------------------------------------------------
- // make sure P_READ_DATA has known initial value
- board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
- fork
- board.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;
- join
- if (board.RP.tx_usrapp.P_READ_DATA != 32'hdead_beef)
- begin
- $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %x",
- $realtime, 32'hdead_beef, board.RP.tx_usrapp.P_READ_DATA);
- test_failed_flag = 1;
- end
- else
- begin
- $display("[%t] : Test PASSED --- Write Data: %x successfully received",
- $realtime, board.RP.tx_usrapp.P_READ_DATA);
- end
- board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
- board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
- end
- 2'b10 : // MEM 32 SPACE
- begin
- $display("[%t] : Transmitting TLPs to Memory 32 Space BAR %x", $realtime,
- board.RP.tx_usrapp.ii);
- //--------------------------------------------------------------------------
- // Event : Memory Write 32 bit 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);//Modified By Jerry
- board.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;
- //--------------------------------------------------------------------------
- // Event : Memory Read 32 bit TLP
- //--------------------------------------------------------------------------
- // make sure P_READ_DATA has known initial value
- board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
- fork
- board.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);//Modified by Jerry
- board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h08, 4'h0, 4'hF);
- board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;
- join
- if (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] })
- begin
- $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %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;
- end
- else
- begin
- $display("[%t] : Test PASSED --- Write Data: %x successfully received",
- $realtime, board.RP.tx_usrapp.P_READ_DATA);
- end
- board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
- board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
- end
- 2'b11 : // MEM 64 SPACE
- begin
- $display("[%t] : Transmitting TLPs to Memory 64 Space BAR %x", $realtime,
- board.RP.tx_usrapp.ii);
- //--------------------------------------------------------------------------
- // Event : Memory Write 64 bit 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;
- //--------------------------------------------------------------------------
- // Event : Memory Read 64 bit TLP
- //--------------------------------------------------------------------------
- // make sure P_READ_DATA has known initial value
- board.RP.tx_usrapp.P_READ_DATA = 32'hffff_ffff;
- fork
- board.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;
- join
- if (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] })
- begin
- $display("[%t] : Test FAILED --- Data Error Mismatch, Write Data %x != Read Data %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;
- end
- else
- begin
- $display("[%t] : Test PASSED --- Write Data: %x successfully received",
- $realtime, board.RP.tx_usrapp.P_READ_DATA);
- end
- board.RP.tx_usrapp.TSK_TX_CLK_EAT(10);
- board.RP.tx_usrapp.DEFAULT_TAG = board.RP.tx_usrapp.DEFAULT_TAG + 1;
- end
- default : $display("Error case in usrapp_tx\n");
- endcase
- end
- $display("[%t] : Finished transmission of PCI-Express TLPs", $realtime);
- if (!test_failed_flag) begin
- $display ("Test Completed Successfully");
- end
- $finish;
- 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空间初始化任务里同时调用了其它几个任务子程序:
- /************************************************************
- Task : TSK_BAR_INIT
- Inputs : None
- Outputs : None
- Description : Initialize PCI core based on core's configuration.
- *************************************************************/
- task TSK_BAR_INIT;
- begin
- 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的配置读写:
- // Determine Range for 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);
- // Read BAR0 Range
- 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:
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文件中。
开发工匠 2019-8-9 16:06
curton 2019-8-7 20:58