tag 标签: Xilinx

相关帖子
相关博文
  • 热度 2
    2019-8-26 17:48
    1791 次阅读|
    0 个评论
    问题描述:EP端发起了DMA写,但是RP侧并未收到DMA写的TLP,但是收到了写完成的中断TLP。 参考了Xilinx官网论坛这个帖子,解决了这个问题: https://forums.xilinx.com/t5/PCI-Express/Problems-of-PCIE-DMA-simulation/m-p/329263 xapp1052只是说要设置Bus Master Enable,实际在仿真(或者实际设计)的时候RP和EP侧都行将该bit置位。
  • 热度 5
    2019-8-15 11:16
    1997 次阅读|
    0 个评论
    32位存储器写任务
    完成剩下的DS端口模型任务函数学习,本文来看看存储32bit写,首先复习下32-bit地址存储器写TLP的头部格式,如下图所示: 图1:32位地址存储器写TLP头部格式 接下来我们来看DS端口模型里是如何使用这个函数的,在启动DMA操作之前都需要对DMA进行复位,这时候需要Host使用存储写操作往设备控制状态寄存器使能复位,具体实现代码如下(下述代码先写1启动复位,再写0,释放复位): DATA_STORE = 8'h01; //reset DATA_STORE = 8'h00; DATA_STORE = 8'h08; //data width path DATA_STORE = 8'h14; //fpga family TSK_TX_MEMORY_WRITE_32(DEFAULT_TAG,DEFAULT_TC, 10'd1,BAR_INIT_P_BAR +8'h00, 4'h0, 4'hF, 1'b0); TSK_TX_CLK_EAT(10); DEFAULT_TAG = DEFAULT_TAG + 1; DATA_STORE = 8'h00; //reset TSK_TX_MEMORY_WRITE_32(DEFAULT_TAG,DEFAULT_TC, 10'd1,BAR_INIT_P_BAR +8'h00, 4'h0, 4'hF, 1'b0); TSK_TX_CLK_EAT(10); DEFAULT_TAG = DEFAULT_TAG + 1; 至于BMD各个设计描述寄存器请查阅xapp1052文档,这里给出各个寄存器地址的截图: 图2:BMD设计描述寄存器地址 ( 注 :很重要,对于初学者来说,理解PCIe应用里各种地址有点困难。图2中寄存器地址是FPGA逻辑为DMA操作定义的寄存器,DMA引擎依据这些寄存器来采取对应的操作,这些寄存器可以通过存储器读写来进行访问。而配置寄存器则是与PCIe核相关,这些寄存器的相对地址和这里的寄存器地址甚至名称都可能一样,但是完全不是一个概念,配置寄存器只能通过Type0/1配置寄存器读写程序访问,且一般是驱动程序发起,电脑上电时BIOS也会发起。) 个人理解,上述寄存器用户可以随意定义,前提是要知会驱动程序以及软件设计人员,上中下层级协调一致即可。当然,对应FPGA设计者来说如果是基于BMD的设计,没有必要从头再来另搞一套,除非用户自己设计自己的DMA控制器。 下面来分析TSK_TX_MEMORY_WRITE_32这个函数,下图对函数输入参数进行了简单说明 对应前面调用该函数时,可以看到,写数据长度是1,即一个DW数据。 LastDW BE和First DW BE这两个字段的使用规则如下。 如果传送的数据长度在一个对界的双字(DW)之内,则Last DW BE字段为0b0000,而First DW BE的对应位置1;如果数据长度超过1DW,Last DW BE字段一定不能为0b0000。PCIe总线使用LastDW BE字段为0b0000表示所传送的数据在一个对界的DW之内。 如果传送的数据长度超过1DW,则First DW BE字段至少有一个位使能。不能出现First DW BE为0b0000的情况。 如果传送的数据长度大于等于3DW,则在First DW BE和Last DW BE字段中不能出现不连续的置1位。 如果传送的数据长度在1DW之内时,在First DW BE字段中允许有不连续的置1位。此时PCIe总线允许在TLP中传送1个DW的第1,3字节或者第0,2字节。 如果传送的数据长度为2DW之内时,则First DW BE字段和Last DW BE字段允许有不连续的置1位 根据以上规则,在发起BDM的DMA操作的时候,多次用该函数,而每次仅对一个寄存器进行写,故只有1个DW数据,所以 Last DW BE字段为0b0000,而First DW BE的对应位置1。此外,函数的地址参数是BAR的基地址+对应寄存器地址(即偏移地址,图2中各个寄存器地址)。
  • 热度 3
    2019-8-14 16:05
    1335 次阅读|
    0 个评论
    在ModelSim里添加带路径的文件
    最近在仿真Xilinx的PCIe,用的是其xapp1052下的参考工程,这个工程基于vivado,默认使用vivado自带的仿真器,如果转换成ModelSim的话,vivado会后台自动生成一系列脚本文件,仿真的时候直接运行批处理文件,该批处理文件会自动调用脚本启动ModelSim进行仿真。 下图是编译源文件的时候vlog使用的语法 由于xapp1052这个设计并不完备,我需要对其进行修改,但是为了让自己能够回到初始状态,又不希望在这个项目上直接修改,所以想到将其pcie_7x_0_example.srcs目录下所有文件拷贝到其它地方再进行修改并仿真。问题是改动位置后上述vlog语法找寻文件的路径就变了,需要做如下更正: 学过dos系统都知道,“..”表示上一级目录,“.”表示本级目录,如果源文件都在脚本目录下,就不需要指定路径。其实还有一个最笨的办法,就是给每个文件提供完整的绝对路径。
  • 热度 8
    2019-8-8 15:19
    1287 次阅读|
    0 个评论
    创建PCIe空间映射子函数
    BAR初始化第一步,即BAR_SCAN子函数了解完了,下面我们了解初始化第二步,即TSK_BUILD_PCIE_MAP函数。再次复习下BAR初始化步骤( 在TSK_BAT_INIT任务里面其实是在模拟BIOS和驱动程序的操作,首先会对BAR寄存器进行赋值,然后会对BAR指向的设备内存进行IO或者mem映射,最后就是对设备的配置寄存器的参数写入。 ): /************************************************************ 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_BUILD_PCIE_MAP源代码有点长,这里就不贴了。其主要目的就是执行存储器或I/O映射算法,并依据Endpoint需求分配Memory 32、Memory 64和I/O空间。具体来说,就是根据上一步 TSK_BAR_SCAN得到的各个BAR的range变量,检查这些range,根据检查结果,判断每个BAR是否被使能、及其对应的映射结果(是MEM32啊还是MEM64啊,或者是I/O空间)。仿真的时候会将检查结果打印如下: 注 :函数 TSK_BUILD_PCIE_MAP在检查正常的时候只会打印上图中的第一句;下面每个BAR空间的检查结果,是在调用函数TSK_DISPLAY_PCIE_MAP的时候打印的。 BAR空间初始化最后一步,看似就是将之前读回的BAR状态重新使用Type0配置写再写回去( 这里应该对应应用程序里的BAR空间基地址的产生,电脑开机扫描获取PCIe的各个BAR的信息,为它们分配地址空间,这里的Program应该是将分配好的地址空间基地址重新写回到BAR寄存器 ),比如对BAR0的写: // Program BAR0 TSK_TX_TYPE0_CONFIGURATION_WRITE(DEFAULT_TAG, 12'h10, BAR_INIT_P_BAR , 4'hF); DEFAULT_TAG = DEFAULT_TAG + 1; TSK_TX_CLK_EAT(100); 这个子函数还对其它BAR空间进行Type0配置写,个人觉得,其它BAR空间都Disable了,写不写都无所谓。值得注意的时候,该子函数最后还对PCI命令寄存器和PCIe器件控制寄存器进行了编程: // Program PCI Command Register TSK_TX_TYPE0_CONFIGURATION_WRITE(DEFAULT_TAG, 12'h04, 32'h00000003, 4'h1); DEFAULT_TAG = DEFAULT_TAG + 1; TSK_TX_CLK_EAT(100); // Program PCIe Device Control Register TSK_TX_TYPE0_CONFIGURATION_WRITE(DEFAULT_TAG, 12'h68, 32'h0000005f, 4'h1); DEFAULT_TAG = DEFAULT_TAG + 1; TSK_TX_CLK_EAT(1000); 注意上述两次写和BAR0写的区别,比如最后一个参数为4‘h1,则表示只有最低字节被Enable了,即只需传递低8位即可。 了解完BAR空间的初始化,后面开始了解正常的PIO仿真,即存储器写和存储器读。
  • 热度 4
    2019-8-8 14:46
    915 次阅读|
    0 个评论
    Type0配置写函数
    在前面了解PCI配置空间、TLP以及Type0配置读的基础上,本文介绍Type0配置写子函数。该子函数产生一个Type0配置写TLP,先来看具体代码: /************************************************************ Task : TSK_TX_TYPE0_CONFIGURATION_WRITE Inputs : Tag, PCI/PCI-Express Reg Address, First BypeEn Outputs : Transaction Tx Interface Signaling Description : Generates a Type 0 Configuration Write TLP *************************************************************/ task TSK_TX_TYPE0_CONFIGURATION_WRITE; input tag_; input reg_addr_; input reg_data_; input first_dw_be_; begin if (trn_lnk_up_n) begin $display(" : Trn interface is MIA", $realtime); $finish(1); end TSK_TX_SYNCHRONIZE(0, 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 COMPLETER_ID_CFG, tag_, 4'b0000, first_dw_be_, // 64 COMPLETER_ID_CFG, 4'b0000, reg_addr_ , 2'b00, // 32 reg_data_ , reg_data_ , reg_data_ , reg_data_ // 64 }; trn_tsof_n <= #(Tcq) 0; trn_teof_n <= #(Tcq) 0; trn_trem_n <= #(Tcq) 2'b00; trn_tsrc_rdy_n <= #(Tcq) 0 ; TSK_TX_SYNCHRONIZE(1, 1, 1); trn_tsof_n <= #(Tcq) 1; trn_teof_n <= #(Tcq) 1; trn_trem_n <= #(Tcq) 2'b00; trn_tsrc_rdy_n <= #(Tcq) 1; end endtask // TSK_TX_TYPE0_CONFIGURATION_WRITE 有了对Type0配置读的了解,那么理解上面的代码就容易很多了,TLP前面32bit和配置读一样,唯一的区别在于TLP最后32bit带了1DW的写数据。写数据通过函数第三个参数调用的时候引入。 需要注意的是,上述代码中两次调用了函数 TSK_TX_SYNCHRONIZE,第一次调用( TSK_TX_SYNCHRONIZE(0, 0, 0); ),只是为了同步trn_clk和trn_tdst_rdyn_n,之后TLP信息被赋值给trn_td。第二次调用( TSK_TX_SYNCHRONIZE(1, 1, 1); )是为了同步信号,也是为了将TLP信息添加到本地buffer,并最终发送到输出log。也就是说,我们在仿真的时候看到很多下图所示的信息都是第二次调用函数 TSK_TX_SYNCHRONIZE的时候处理,第二次调用除了在仿真的时候的打印输出下图所示的信息外,还将TLP信息Log到输出文件(tx.dat和rx.dat) 这里有个疑问是在TSK_BAR_SCAN函数中,对每个BAR先使用Type配置写,写入的数据是”P_ADDRESS_MASK = 32'hffff_ffff;“,源代码给出的注释是对BAR空间写PCI_MASK来找到range。接着使用Type0配置读刚刚被写入MASK值的BAR空间,读回的数据保存在BAR_INIT_P_BAR_RANGE 里,也就是所谓“ 找到range ”。我的问题是,为什么对BAR空间写入MASK后,再对其进行type0读就可以得到range?
相关资源
广告