PCIE 仿真

7 Series Intergrated Block for PCI Express仿真

仿真工具

vivado 22018.3 、modelsim 10.6d
用vivado自带的仿真工具也可以,不过速度上会慢许多

PCIE 3种转换接口比较

在这里插入图片描述
PCIE IP的3种转换IP如上图
其中AXI memory和DMA/Bridege 是把PCIE转换为数据访问总线AXI4和配置总线;
7 seres Intergrated Block十把PCIE总线转换为配置总线和AXIS总线,在对PCIE配置完成后,使用AXIS对数据进行传输即可。本文主要对7 seres Intergrated Block for PCI Express的仿真模块进行粗略的说明。
该IP的仿真使用xilinx自己的demo进行。官方demo如何搭建此处不做赘述,网上有很多教程。

工程说明

官方仿真工程结构
官方仿真工程如上图所示,但这并不是全部。做PCIE仿真的时候需要明白它最简单的拓扑结构
PCIE拓扑图
如上图,PCIE常常采用树形拓扑结构,一般由根组件(Root Complex),交换设备(Switch),终端设备(Endpoint)等类型的PCIe设备组成。
Root Complex: 根桥设备,是PCIe最重要的一个组成部件; Root Complex主要负责PCIe报文的解析和生成。RC接受来自CPU的IO指令,生成对应的PCIe报文,或者接受来自设备的PCIe TLP报文,解析数据传输给CPU或者内存。
Switch: PCIe的转接器设备,目的是扩展PCIe总线。和PCI并行总线不同,PCIe的总线采用了高速差分总线,并采用端到端的连接方式, 因此在每一条PCIe链路中两端只能各连接一个设备, 如果需要挂载更多的PCIe设备,那就需要用到switch转接器。switch在linux下不可见,软件层面可以看到的是switch的上行口(upstream port, 靠近RC的那一侧) 和下行口(downstream port)。
一般而言,一个switch 只有一个upstream port, 可以有多个downstream port.
PCIe endponit: PCIe终端设备,是PCIe树形结构的叶子节点。比如网卡,NVME卡,显卡都是PCIe ep设备。
在此例程中,可以把RP理解为“CPU+Root Comples”,EP理解为FPGA,其完成的仿真工程结构如下:
完整仿真结构图

仿真流程

根据仿真日志:
Inspecting Core Configuration Space
Setting Core Configuration Space
对应的任务有两个:
TSK_BAR_SCAN、TSK_BAR_PROGRAM;这两个任务对应的子任务是
TSK_TX_TYPE0_CONFIGURATION_WRITE、TSK_TX_TYPE0_CONFIGURATION_READ
所有任务均在“pci_exp_usrapp_tx.v”这个文件下。
子任务里面的内容可以看一下:

  task TSK_TX_TYPE0_CONFIGURATION_WRITE;
    input    [7:0]    tag_;
    input    [11:0]    reg_addr_;
    input    [31:0]    reg_data_;
    input    [3:0]    first_dw_be_;
    begin
        if (trn_lnk_up_n) begin

            $display("[%t] : Trn interface is MIA", $realtime);
            $finish(1);

        end

        TSK_TX_SYNCHRONIZE(0, 0);

        trn_td             <= #(Tcq)   {
                                       1'b0,
                                       2'b10,
                                       5'b00100,
                                       1'b0,
                                       3'b000,
                                       4'b0000,
                                       1'b0,
                                       1'b0,
                                       2'b00,
                                       2'b00,
                                       10'b0000000001, // 32
                                       REQUESTER_ID, //COMPLETER_ID_CFG,
                                       tag_,
                                       4'b0000,
                                       first_dw_be_    // 64
                                       };

        trn_tsof_n         <= #(Tcq)   0;
        trn_tsrc_rdy_n     <= #(Tcq)   0 ;

        TSK_TX_SYNCHRONIZE(1, 0);

        trn_td             <= #(Tcq)   {
                                       COMPLETER_ID_CFG,
                                       4'b0000,
                                       reg_addr_[11:2],
                                       2'b00,            // 32
                                       reg_data_[7:0],
                                       reg_data_[15:8],
                                       reg_data_[23:16],
                                       reg_data_[31:24]  // 64
                                       };

        trn_tsof_n         <= #(Tcq)   1;
        trn_teof_n         <= #(Tcq)   0;
        trn_trem_ni         <= #(Tcq)   8'h00;

        TSK_TX_SYNCHRONIZE(1, 1);

        trn_teof_n         <= #(Tcq)   1;
        trn_trem_ni         <= #(Tcq)   0;
        trn_tsrc_rdy_n     <= #(Tcq)   1;

    end
endtask // TSK_TX_TYPE0_CONFIGURATION_WRITE

其中有4个输入参数,
tag_:标志位
reg_addr_:寄存器地址
reg_data_:寄存器数据
first_dw_be_:xxxx(作用还不清楚)

task TSK_TX_TYPE0_CONFIGURATION_READ;
    input    [7:0]    tag_;
    input    [11:0]    reg_addr_;
    input    [3:0]    first_dw_be_;
    begin
        if (trn_lnk_up_n) begin

            $display("[%t] : Trn interface is MIA", $realtime);
            $finish(1);

        end

        TSK_TX_SYNCHRONIZE(0, 0);

        trn_td             <= #(Tcq)    {
                                        1'b0,
                                        2'b00,
                                        5'b00100,
                                        1'b0,
                                        3'b000,
                                        4'b0000,
                                        1'b0,
                                        1'b0,
                                        2'b00,
                                        2'b00,
                                        10'b0000000001,  // 32
                                        REQUESTER_ID, //COMPLETER_ID_CFG,
                                        tag_,
                                        4'b0000,
                                        first_dw_be_     // 64
                                        };

        trn_tsof_n         <= #(Tcq)    0;
        trn_teof_n         <= #(Tcq)    1;
        trn_trem_ni         <= #(Tcq)    0;
        trn_tsrc_rdy_n     <= #(Tcq)    0 ;

        TSK_TX_SYNCHRONIZE(1, 0);

        trn_td             <= #(Tcq)    {
                                        COMPLETER_ID_CFG,
                                        4'b0000,
                                        reg_addr_[11:2],
                                        2'b00,
                                        32'b0
                                        };

        trn_tsof_n         <= #(Tcq)    1;
        trn_teof_n         <= #(Tcq)    0;
        trn_trem_ni         <= #(Tcq)    8'h0F;
        trn_tsrc_rdy_n     <= #(Tcq)    0 ;

        TSK_TX_SYNCHRONIZE(1, 1);

        trn_teof_n         <= #(Tcq)    1;
        trn_trem_ni         <= #(Tcq)    0;
        trn_tsrc_rdy_n     <= #(Tcq)    1;

    end
endtask // TSK_TX_TYPE0_CONFIGURATION_READ

其中有3个输入参数,
tag_:标志位
reg_addr_:寄存器地址
first_dw_be_:xxxx(作用还不清楚)

根据TSK_BAR_SCAN、TSK_BAR_PROGRAM的内部内容可知:
任务内部是先执行一次TSK_TX_TYPE0_CONFIGURATION_WRITE,再执行一次TSK_TX_TYPE0_CONFIGURATION_WRITE,也就是每个配置寄存器是在RP端先写一次,然后再读一次,配置完成,再写一个数据进行读写验证。
写数据,也就是memory 到 space的验证。"sample_tests1.vh"文件中。
在board.RP.tx_usrapp.BAR_INIT_P_BAR_ENABLED[board.RP.tx_usrapp.ii] = 2‘b10
的情况下:

       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);
                      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);
                         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

测试数据为0x04030201,TSK_TX_MEMORY_WRITE_32 写一次,TSK_TX_MEMORY_READ_32读一次,和配置寄存器一样的套路。把读回来的额数据和写入的数据进行比较,判断仿真是否成功,至此,整个仿真流程完成。

仿真信号分析

运行上述仿真,一个完整的仿真时序如下:
一个完整的仿真时序
查看整个仿真时序图,RP端得到tx_valid信号会紧跟一个rx_valid信号,是因为寄存器配置流程中是一个写寄存器操作会紧跟一个读寄存器操作。另外RP端的tx_valid在寄存器配置阶段,EP端会有一个tx_cfg_req信号响应。
查询官方手册pg054-7series-pcie,对该信号的说明如下:
tx_cfg_req信号说明
大致意思就是配置信号接收完成指示。
另外在RP端发送测试数据,EP端的rx分别分别接收了两次,第一次是测试数据,第二次是数据读回命令的接收;EP的tx发送了1次,发送的的RP端写入的数据;另外RP的rx接收了一次数据,接收的是EP传回来的数据。
EP端tx回传数据
接收帧格式
如上图,设置突发长度是1,后面紧跟1个数据,mem 32模式。

不过把长度设置为200,接收那里却不能正常接收,应该还是和某个配置项有关。这个问题还在查找。

              board.RP.tx_usrapp.TSK_TX_MEMORY_WRITE_32(board.RP.tx_usrapp.DEFAULT_TAG,
                          board.RP.tx_usrapp.DEFAULT_TC, *10'd200*,
                          board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h10, 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'd200***,
                             board.RP.tx_usrapp.BAR_INIT_P_BAR[board.RP.tx_usrapp.ii][31:0]+8'h10, 4'h0, 4'hF);
                         board.RP.tx_usrapp.TSK_WAIT_FOR_READ_DATA;
                      join

长度设置为200
收发具体数据:
长度200 接收到的数据
设置发送长度为200接收到的数据
红色圈内的数据未发送过去或发送过程中丢失
发送端时序
最后两个数据和接收端相符合
发送端最后两个数据,与接收端符合

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值