1.框架总览

平台:vivado 2016.4

FPGA:A7

在实际应用中,我们几乎不可能自己去编写接口协议,所以在IP核的例程上进行修改来适用于项目是个不错的选择。

通过vivado 中有关PCIe的IP核,生成相应的例程,综合之后可以得到如下图的工程结构。



如果在自己的项目中直接使用IP核的话,生成的只有pcie_7x_0这个模块,在应用层面还需要编写相应的解析和组装模块。好在该例程已经帮我们把这部分模块编写好了。例程简单的工作流程图如下图所示。

关于PCIe入门的简单协议介绍,可以参考博文 FPGA实战操作(2) -- PCIe总线(协议简述)

2.应用层模块设计分析(pcie_app_7x)

例程在PCIe核的基础上,已经为用户设计好了应用层模块。用户在使用的过程中只需要在应用层上稍加修改(例程是个闭环系统,需要将收发模块的部分接口对接到自己项目中),就可以将整个例程移植到自己的项目中了。

下面主要分析应用层模块中的PIO_RX_ENGINE、PIO_TX_ENGINE这两个核心模块。

2.1 PIO_RX_ENGINE

PIO_RX_ENGINE接口层面主要实现三个功能:将PCIe IP核传递过来的TLP包解析,之后将结果一部分用于控制存储器读取数据,一部分解析结果(协议有效字段)发送给PIO_TX_ENGINE,在需要反馈报文时用。

有关数据的解析,主要由下图状态机控制完成。程序中支持64位宽和128位宽,通过C_DATA_WIDTH来判断执行不同部分。

状态机状态state首先进入PIO_RX_RST_STATE,在PIO_RX_RST_STATE中完成数据关键字段的解析,根据解析的结果来执行中间几个状态(红线),这几个执行状态主要根据命令来完成相应的操作,执行完毕后进入PIO_RX_WAIT_STATE。

下面对几个关键状态里的内容进行简单分析。

2.1.1 PIO_RX_RST_STATE

程序首先进入PIO_RX_RST_START 状态,这是一个复位状态。在复位状态中,首先判断sop 信号是否有效,sop 信号是TLP开始的信号,若这个信号无效,则程序会一直在复位状态中直到sop 信号有效;若sop 信号有效,则执行嵌套的一段case语句,case的条件是m_axis_rx_tdata[30:24],研究TLP协议包会发现,该字段对应的是Fmt+Type字段,代表TLP确定的事务类型,实际上就是命令操作,让你干什么。

事务类型的case中,只介绍PIO_RX_MEM_RD32_FMT_TYPE,实际即32位寻址的读事务。在PIO_RX_MEM_RD32_FMT_TYPE状态中,按照TLP协议包格式,将各字段数据锁存入相应的寄存器中,可以参见后面的注释。其它的事务类型的操作都大同小异,就是锁存所需字段,执行相应的操作。

case (m_axis_rx_tdata[30:24])   // TLP中 Fmt Type的判断
// 00000001_10100000_00001001_00001111_01000000_00000000_00000000_00000001
// 00000001_10100000_00001010_00001111_00000000_00000000_00000000_00000001
// m_axis_rx_tdata[30:24] = 1000000 ,执行 PIO_RX_MEM_WR32_FMT_TYPE PIO_RX_MEM_RD32_FMT_TYPE : begin // 存储器读请求,3个DW,不带数据
tlp_type <= #TCQ m_axis_rx_tdata[31:24]; // 锁存TLP类型,即Fmt+Type
req_len <= #TCQ m_axis_rx_tdata[9:0]; // 锁存TLP数据包长度,单位是DW
m_axis_rx_tready <= #TCQ 1'b0; // 未准备好
if (m_axis_rx_tdata[9:0] == 10'b1) begin // 若数据长度为1DW
req_tc <= #TCQ m_axis_rx_tdata[22:20];
req_td <= #TCQ m_axis_rx_tdata[15];
req_ep <= #TCQ m_axis_rx_tdata[14];
req_attr <= #TCQ m_axis_rx_tdata[13:12];
req_len <= #TCQ m_axis_rx_tdata[9:0];
req_rid <= #TCQ m_axis_rx_tdata[63:48]; // Reverse ID
req_tag <= #TCQ m_axis_rx_tdata[47:40]; // Tag字段的长度决定发送端能够暂存多少
req_be <= #TCQ m_axis_rx_tdata[39:32]; // TLP使用 last DW BE和first DW BE这两个字段
state <= #TCQ PIO_RX_MEM_RD32_DW1DW2; // 表示从存储器中读取DW1和DW2 end // if (m_axis_rx_tdata[9:0] == 10'b1)
else begin
state <= #TCQ PIO_RX_RST_STATE;
end // if !(m_axis_rx_tdata[9:0] == 10'b1)
end // PIO_RX_MEM_RD32_FMT_TYPE

注意:在介绍协议的时候,都是基于DW(32位)介绍,但在通过AXI4总线进行数据交换时,采用的是64位宽或者128位宽,所以要注意一下,数据大小端拼接。

2.1.2 PIO_RX_MEM_RD32_DW1DW2

在执行完成PIO_RX_MEM_RD32_FMT_TYPE状态之后,主状态机跳转至PIO_RX_MEM_RD32_DW1DW2状态,表示要执行读操作的配置工作。

由PCIe通信机制可知,存储器读请求是需要反馈完成报文的,而且这个完成报文反馈包括两方面:第一,TLP协议有些固定字段,要反馈;第二,从存储器指定地址下的数据,要反馈。

req_addr 表示需要读取的存储器的地址,req_compl 表示需要发送完成报文,req_compl_wd 表示完成报文中包含数据,然后跳转到PIO_RX_WAIT_STATE 状态。

PIO_RX_WAIT_STATE 状态没什么好说的。

2.2 PIO_TX_ENGINE

PIO_TX_ENGINE结构与PIO_RX_ENGINE类似,也是由状态机完成,主要执行组装封包功能。

着重看一下PIO_TX_CPLD_QW1_FIRST和PIO_TX_CPLD_QW1两个状态。

2.2.1 PIO_TX_CPLD_QW1_FIRST

PIO_TX_CPLD_QW1_FIRST : begin   // 完成报文头标的第一个DW
if (s_axis_tx_tready) begin
s_axis_tx_tlast <= #TCQ 1'b0;
s_axis_tx_tdata <= #TCQ { // Bits
completer_id, // 16
{3'b0}, // 3 完成状态,000--成功完成
{1'b0}, // 1
byte_count, // 12
{1'b0}, // 1
(req_compl_wd_q ? // 看此包带不带数据
PIO_CPLD_FMT_TYPE :
PIO_CPL_FMT_TYPE), // 7
{1'b0}, // 1
req_tc, // 3
{4'b0}, // 4
req_td, // 1
req_ep, // 1
req_attr, // 2
{2'b0}, // 2
req_len // 10
};
s_axis_tx_tkeep <= #TCQ 8'hFF;
state <= #TCQ PIO_TX_CPLD_QW1_TEMP;
end
else
state <= #TCQ PIO_TX_RST_STATE;
end //PIO_TX_CPLD_QW1_FIRST

这个状态中传输第一帧(64bit)数据,由TLP协议可知,第一帧数据主要反馈的Head信息,即头标的前2DW数据,其中通过req_compl_wd_q 信号判定这个完成包是否带有数据,同时因为这一帧信号都是有效的信号,所以s_axis_tx_tkeep 为FF。

2.2.2 PIO_TX_CPLD_QW1

PIO_TX_CPLD_QW1 : begin
if (s_axis_tx_tready) begin
s_axis_tx_tlast <= #TCQ 1'b1;
s_axis_tx_tvalid <= #TCQ 1'b1;
// Swap DWORDS for AXI
s_axis_tx_tdata <= #TCQ { // Bits
rd_data, // 32
req_rid, // 16
req_tag, // 8
{1'b0}, // 1
lower_addr // 7
};
// Here we select if the packet has data or
// not. The strobe signal will mask data
// when it is not needed. No reason to change
// the data bus.
if (req_compl_wd_q)
s_axis_tx_tkeep <= #TCQ 8'hFF;
else
s_axis_tx_tkeep <= #TCQ 8'h0F; compl_done <= #TCQ 1'b1;
compl_busy_i <= #TCQ 1'b0;
state <= #TCQ PIO_TX_RST_STATE;
end // if (s_axis_tx_tready)
else
state <= #TCQ PIO_TX_CPLD_QW1;
end // PIO_TX_CPLD_QW1

在这个状态中,首先通过s_axis_tx_tready 信号判断从设备是否准备好接受信号;由于完成包由3DW标头和1DW的数据构成,总共2帧,所以这一帧是最后一帧,因此此时设置s_axis_tx_tlast为1表明这是最后一帧;然后设置s_axis_tx_tvalid为1表明此时主设备准备好发送数据;接着就根据完成包格式拼接发送数据。拼接完成后通过req_compl_wd_q设置s_axis_tx_tkeep信号,由于一次传输64bit,所以第二帧刚好1DW标头+1DW数据,这一帧都有效,所以s_axis_tx_tkeep为FF,如果这个完成包不带数据,即req_compl_wd_q无效,则最后一帧数据中只有1DW标头,那么s_axis_tx_tkeep就为0F。

至此这个带数据的完成包就发送完成了,所以设置compl_done有效,这个信号返回到接收引擎中,使得接收引擎准备接收下一个TLP,设置compl_busy_i无效,说明又可以发送完成包了,同时状态机跳转至PIO_TX_RST_STATE状态。

参考文献

  1. PCIe学习(一):PCIe基础及生成PIO例程分析——judyzhong
  2. 《LogiCORE™ IP Endpoint for PCI Express® v3.7》(UG185)

FPGA实战操作(2) -- PCIe总线(例程设计分析)的更多相关文章

  1. FPGA实战操作(2) -- PCIe总线(协议简述)

    目录 1. PCIe基础知识 2. 事务层协议 2.1 数据包结构 2.2 帧头含义详述 3. 报文举例 3.1 寄存器读报文 3.2 完成报文 4. 机制简述 4.1 Non-Posted和Post ...

  2. 4.1 PCIe总线的基础知识

    与PCI总线不同,PCIe总线使用端到端的连接方式,在一条PCIe链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端.PCIe总线除了总线链路外,还具有多个层次,发送端发送数据时将通 ...

  3. 012 PCIe总线的基础知识

    一.PCIe总线的基础知识 与PCI总线不同,PCIe总线使用端到端的连接方式,在一条PCIe链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端.PCIe总线除了总线链路外,还具有多 ...

  4. 第5章 PCIe总线的事务层

    事务层是PCIe总线层次结构的最高层,该层次将接收PCIe设备核心层的数据请求,并将其转换为PCIe总线事务,PCIe总线使用的这些总线事务在TLP头中定义.PCIe总线继承了PCI/PCI-X总线的 ...

  5. 第4章 PCIe总线概述

    随着现代处理器技术的发展,在互连领域中,使用高速差分总线替代并行总线是大势所趋.与单端并行信号相比,高速差分信号可以使用更高的时钟频率,从而使用更少的信号线,完成之前需要许多单端并行数据信号才能达到的 ...

  6. 017 PCIe总线的事务层(一)

    一.PCIe总线的事务层 事务层是PCIe总线层次结构的最高层,该层次将接收PCIe设备核心层的数据请求,并将其转换为PCIe总线事务,PCIe总线使用的这些总线事务在TLP头中定义.PCIe总线继承 ...

  7. 基于 FPGA 的 PCIE 总线 Linux 驱动设计

    硬件平台 Kintex ®-7 family of FPGAs Intel X86 软件平台 Linux 4.15.0-36-generic #39~16.04.1-Ubuntu Xilinx xap ...

  8. FPGA实战操作(1) -- SDRAM(Verilog实现)

    对SDRAM基本概念的介绍以及芯片手册说明,请参考上一篇文章SDRAM操作说明. 1. 说明 如图所示为状态机的简化图示,过程大概可以描述为:SDRAM(IS42S16320D)上电初始化完成后,进入 ...

  9. FPGA实战操作(1) -- SDRAM(操作说明)

    SDRAM是做嵌入式系统中,常用是的缓存数据的器件.基本概念如下(注意区分几个主要常见存储器之间的差异): SDRAM(Synchronous Dynamic Random Access Memory ...

随机推荐

  1. 日常开发用Windows 好还是 Ubuntu好?

    最近打算给电脑重新装系统,纠结了很久,不知道应该是换Windows还是Ubuntu,今天通过我自身的体验,来为大家分析一下,日常开发环境到底是用Windows和Ubuntu. [系统介绍] Windo ...

  2. Linux服务器开发:工具

    预处理 将所有#defined删除,并且展开 处理所有条件预处理指令 处理#include,将被包含的文件插入到该预编译指令的位置 过滤所有的//./**/ 保留所有#pragma编译指令 编译 词法 ...

  3. jvm监控工具jconsole进行远程监控配置

    [环境] SUSE linux11 + jdk1.6 + tomcat7 [场景] 最近在做性能测试,想通过我本地(win7)上的jdk来远程监控上述服务器的jvm相关信息. [配置] 配置上述服务器 ...

  4. Scal(三)——类与对象

    Scala快速入门(三)--类与对象 一.类 这边类的模板如下,和Java很像 object a { def main(args: Array[String]): Unit = { val perso ...

  5. java——比较难和底层的面试题

    链接地址:https://mp.weixin.qq.com/s/lnbCysCQgfjF_kcB83KQZg 这是一个在线教育机构的文章,感觉大部分都不会,太难了. 一.自我介绍 二.多线程相关: 线 ...

  6. 异步消息处理机制相关面试问题-AsyncTask面试问题详解

    什么是AsyncTask: 它本质上是一个封装了线程池和handler的异步框架. AsyncTask的使用方法: 三个参数: 五个方法: AsyncTask的内部原理: AsyncTask的注意事项 ...

  7. 安装WIN10+Ubuntu18.04安装教程(实测有效)

    转载原文链接:https://www.cnblogs.com/masbay/articles/10745170.html 安装过程中尤其注意分区时候的挂载点一定要选对!!!选择Ubuntu的EFI所在 ...

  8. [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)] 项目实战 实战2:命令行工具minigrep 我们继续开发我们的minigrep. 我们现 ...

  9. 强大的Visual Studio插件CodeRush全新发布v19.2,助力VS开发

    CodeRush是一个强大的Visual Studio .NET 插件,它利用整合技术,通过促进开发者和团队效率来提升开发者体验.CodeRush能帮助你以极高的效率创建和维护源代码.Consume- ...

  10. 10-SQLServer中统计信息的使用

    一.总结 1.网址https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-stats-tr ...