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常常采用树形拓扑结构,一般由根组件(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_;beginif (trn_lnk_up_n) begin$display("[%t] : Trn interface is MIA", $realtime);$finish(1);endTSK_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, // 32REQUESTER_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, // 32reg_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_;beginif (trn_lnk_up_n) begin$display("[%t] : Trn interface is MIA", $realtime);$finish(1);endTSK_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, // 32REQUESTER_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 SPACEbegin$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 valueboard.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);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] })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;endelsebegin$display("[%t] : Test PASSED --- Write Data: %x successfully received",$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;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,对该信号的说明如下:

大致意思就是配置信号接收完成指示。
另外在RP端发送测试数据,EP端的rx分别分别接收了两次,第一次是测试数据,第二次是数据读回命令的接收;EP的tx发送了1次,发送的的RP端写入的数据;另外RP的rx接收了一次数据,接收的是EP传回来的数据。


如上图,设置突发长度是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 valueboard.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'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接收到的数据

发送端时序

发送端最后两个数据,与接收端符合
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
