系列文章目录
PCI Express 体系结构导读摘录(一)
PCI Express 体系结构导读摘录(二)
PCI Express 体系结构导读摘录(三)
PCI Express 体系结构导读摘录(四)
文章目录
- 系列文章目录
- 第 7 章 PCIe 总线的数据链路层与物理层
- 7. 1 数据链路层的组成结构
- 7. 1. 1 数据链路层的状态
- 7. 1. 2 事务层如何处理 DL_Down 和 DL_Up 状态
- 7. 1. 3 DLLP 的格式
- 7. 2 ACK / NAK 协议
- 7. 2. 1 发送端如何使用 ACK / NAK 协议
- 7. 2. 2 接收端如何使用 ACK / NAK 协议
- 7. 2. 3 数据链路层发送报文的顺序
- 7. 3 物理层简介
- 7. 3. 1 PCIe 链路的差分信号
- 7. 3. 2 物理层的组成结构
第 7 章 PCIe 总线的数据链路层与物理层
PCIe 总线的数据链路层处于事务层和物理层之间, 主要功能是保证来自事务层的 TLP 在 PCIe 链路中的正确传递, 为此数据链路层定义了一系列数据链路层报文, 即 DLLP。 数据链路层使用了容错和重传机制保证数据传送的完整性与一致性, 此外数据链路层还需要对 PCIe 链路进行管理与监控。 数据链路层将从物理层中获得报文, 并将其传递给事务层; 同 时接收事务层的报文, 并将其转发到物理层。
与事务层不同, 数据链路层主要处理端到端的数据传送。 在事务层中, 源设备与目标设备间的传送距离较长, 设备之间可能经过若干级 Switch; 而在数据链路层中, 源设备与目标设备在一条 PCIe 链路的两端。 因此本章在描述数据链路层时, 将使用发送端与接收端的概 念, 而不再使用源设备与目标设备。
物理层是 PCIe 总线的最底层, 也是 PCIe 总线体系结构的核心。 在物理层中涉及许多与差分信号传递有关的模拟电路知识。 PCIe 总线的物理层由逻辑层和电气层组成, 其中电气层更为重要。 在 PCIe 总线的物理层中, 使用 LTSSM 状态机维护 PCIe 链路的正常运转, 该状态机的迁移过程较为复杂, 在第 8 章将详细介绍该状态机。
7. 1 数据链路层的组成结构
数据链路层使用 ACK / NAK 协议发送和接收 TLP, 由发送部件和接收部件组成。 其中发送部件由 Replay Buffer、 ACK / NAK DLLP 接收逻辑和 TLP 发送逻辑组成; 而接收部件由 “Error Check” 逻辑、 ACK / NAK 发送逻辑和 TLP 接收逻辑组成。 数据链路层的拓扑结构如 图 7-1 所示。 在该图中含有两个 PCIe 设备, 分别为 Device A 和 Device B, 使用的 PCIe 链路 为 Device A 的发送链路, 同时也为 Device B 的接收链路。
实际上每个 PCIe 设备的数据链路层都含有发送部件和接收部件。 而上图为简化起见, 仅含有 Device A 的发送部件和 Device B 的接收部件, 即 Device A 发送链路两端使用的两个部件。 Device A 和 Device B 也具有接收部件和发送部件, 这两个部件由 Device B 的发送链路使用, Device B 发送链路的工作原理与 Device A 类似, 本节对此不做详细介绍。
当 PCIe 设备进行数据传递时, 首先在事务层中产生 TLP, 然后通过事务层将这个 TLP 发送给数据链路层, 数据链路层将这个 TLP 加上 Sequence 前缀和 LCRC 后缀后, 首先将这个 TLP 放入到 Replay Buffer 中, 然后再发送到物理层。
目标设备 (Device B) 从物理层接收 TLP 时, 将首先获得带前后缀的 TLP, 该 TLP 经过数据链路层传递给事务层时, 将被去掉 Sequence 前缀和 LCRC 后缀。 在数据链路层中, TLP 的格式如图 7-2 所示。
数据链路层使用 ACK / NAK 协议保证 TLP 的正确传送, ACK / NAK 协议是一种滑动窗口协议, 该协议的详细介绍见第 7. 2 节。 其中 Sequence 前缀存放当前 TLP 的序列号, 滑动窗口协议需要使用这个序列号。 该序列号可以循环使用, 但在同一个时间段内, 一条 PCIe 链路不能含有 Sequence 前缀相同的多个 TLP。 而 LCRC 后缀存放当前 TLP 的校验和。
PCIe 总线的数据链路层使用 Replay Buffer 和 Error Check 部件共同保证数据传送的可靠性和完整性。 来自事务层的 TLP 首先暂存在 Replay Buffer 中, 然后发送到目标设备。 源设备的数据链路层根据来自目标设备的 ACK / NAK DLLP 报文决定是重发这些 TLP, 还是清除保存在 Replay Buffer 中的 TLP 。
Replay Buffer 的大小决定了事务层可以暂存在数据链路层的报文数, Replay Buffer 的容量越大, 在 PCIe 设备发送流水线中容纳的报文越多, 从而也容易保证流水线不会因为发送部件出现 underrun 而中断, 但是 Replay Buffer 的容量越大, 占用的系统资源也越多, 从而影响 PCIe 设备的功耗。 在一个实际应用中, 芯片设计者需要根据 PCIe 链路的延时确定数据链路层 Replay Buffer 的大小, 在第 12. 4. 1 节中将进一步介绍 Replay Buffer 的大小与 PCIe 链路延时间的关系。
在 PCIe 设备的数据链路层中, 还含有一个 Error Check 单元。 PCIe 设备使用 Error Check 单元检查接收到的 TLP, 并决定如何向对端设备进行报文回应。 如果 TLP 被正确接收, PCIe 设备将向对端设备发送 ACK DLLP(数据链路层为提高 PCIe 链路的利用率, 并不会每成功接收一个 TLP 后, 都发送一个 ACK DLLP) ; 如果 TLP 没有被正确接收, PCIe 设备将向对端设备发送 NAK DLLP。
除了 ACK / NAK DLLP 之外, 数据链路层还定义了一系列数据链路层报文 DLLP, 以保证 PCIe 链路的正常工作。 这些 DLLP 都产生于数据链路层, 并终止于数据链路层, 并不会传送到事务层。 有关 DLLP 格式的详细描述见第 7. 1. 3 节。
7. 1. 1 数据链路层的状态
数据链路层需要通过物理层监控 PCIe 链路的状态, 并维护数据链路层的 “控制与管理状态机” (Data Link Control and Management State Machine, DLCMSM) 。 DLCMSM 状态机可以从物理层获得以下与当前 PCIe 链路相关的状态。
- DL_Inactive 状态。 物理层通知数据链路层当前 PCIe 链路不可用。 在当前 PCIe 链路的对端没有连接任何 PCIe 设备, 或者没有检测到对端设备的存在时, 数据链路层处于该状态。
- DL_Init 状态。 物理层通知数据链路层当前 PCIe 链路可用, 且物理层正处于链路初始化状态, 此时数据链路层不能接收或者发送 TLP 和 DLLP。 此时 PCIe 链路首先需要初始化 VC0 的流量控制机制, 然后再对其他虚通路进行流量控制的初始化。 有关流量控制的详细描述见第 9 章。
- DL_Active 状态。 当前 PCIe 链路处于正常工作模式。 此时物理层已完成 PCIe 链路训练或者重训练。 有关链路训练的详细描述见第 8 章。
DLCMSM 状态机的迁移模型如图 7-3 所示。
DLCMSM 状态机除了可以使用上述状态位, 从物理层获得当前 PCIe 链路状态外, 还可以使用以下状态位, 向事务层通知数据链路层所处的状态。 事务层通过这些状态位获知数据链路层所处的工作状态。
- DL_Down。 数据链路层处于该状态时, 表示在 PCIe 链路的对端没有发现其他设备。 当数据链路层处于 DL_Inactive 状态时, 该状态位有效。 值得注意的是 DL_Down 有效时, 并不意味着对端不存在物理设备。 数据链路层仅是使用该状态位通知事务层, 暂时没有从对端中发现 PCIe 设备, 需要进一步检测。
- DL_Up。 数据链路层处于该状态表示在 PCIe 链路的对端连接了其他设备。 当数据链路层处于 DL_Active 状态时, 该状态位有效。
当数据链路层收到物理层的状态信息后, DLCMSM 状态机将进行状态转换, 并向事务 层通知 PCIe 链路的状态。 如果在 PCIe 链路的两端都连接着 PCIe 设备, 那么这两个 PCIe 设备的数据链路层, 在绝大多数时间内状态相同。 数据链路层各个状态的详细说明, 及 PCIe 链路的状态迁移过程如下。
- DL_Inactive 状态
当 PCIe 设备复位时, 将进入该状态。 值得注意的是, 只有传统复位方式才能使 PCIe 设备进入 DL_Inactive 状态, 而 FLR 方式并不会影响 DLCMSM 状态机。
当 PCIe 设备从复位状态进入 DL_Inactive 状态时, 将对 PCIe 数据链路层进行彻底复位, 将与 PCIe 链路相关的寄存器置为复位值, 并丢弃在 Replay Buffer 中保存的所有报文。 当 PCIe 设备处于 DL_Inactive 状态时, 数据链路层将向事务层提交 DL_Down 状态信息, 并丢弃来自数据链路层和物理层的所有 TLP, 而且不接收对端设备发送的 DLLP。
PCIe 设备的物理层设置了一个 LinkUp 位, 该位为 1 时表示 PCIe 链路的对端与一个 PCIe 设备相连。 当物理层的 LinkUp 状态位为 1, 而且事务层没有禁用当前 PCIe 链路时, PCIe 数据链路层将从 DL_Inactive 状态迁移到 DL_Init 状态。
PCIe 设备在进行链路训练时, 将检查 PCIe 链路的对端是否存在 PCIe 设备, 如果对端不 存在 PCIe 设备, 物理层的 LinkUp 位将为 0, 此时数据链路层将一直处于 DL_Inactive 状态。 系统软件可以设置 Switch 下游端口 Link Control 寄存器的 “ Link Disable” 位为 1, 禁用该端 口连接的 PCIe 链路, 此时即便 PCIe 链路对端存在 PCIe 设备, 数据链路层的状态也仍然为 DL_Inactive。
- DL_Init 状态
当数据链路层处于 DL_Init 状态时, 将对 PCIe 链路的虚通道 VC0 进行流量控制初始化。 在 PCIe 总线中, 流量控制的初始化分为两个阶段, 分别为 FC_INIT1 和 FC_INIT2。 在流量控制的 FC_INIT1 阶段, 数据链路层将向事务层提交 DL_Down 状态信息; 而在流量控制的 FC_ 加粗样式INIT2 阶段, 数据链路层将向事务层提交 DL_Up 状态信息。 流量控制的初始化部分详见第 9. 3. 3 节。
当 PCIe 链路处于 DL_Down 状态时, 发送端可以丢弃任何没有被 ACK / NAK 确认的 TLP, 此时数据链路层几乎不会受到事务层的干扰, 从而可以保证流量控制初始化的正常进行。 这 也是 PCIe 链路的流量控制分为 FC_INIT1 和 FC_INIT2 的主要原因。
当 VC0 的流量控制初始化完毕, 而且物理层的 LinkUp 状态位为 0b1 时, 数据链路层将 从 DL_Init 状态迁移到 DL_Active 状态; 如果在进行流量控制初始化时, 物理层的 LinkUp 状 态位被更改为 0b0 时, 数据链路层将从 DL_Init 状态迁移到 DL_Inactive 状态。
- DL_Active 状态
当数据链路层处于 DL_Active 状态时, PCIe 链路可以正常工作, 此时数据链路层可以从 事务层和物理层正常接收和发送 TLP, 并处理 DLLP, 此时数据链路层向事务层提交 DL_Up 状态信息。
当发生以下事件后, 数据链路层可以从 DL_Active 状态迁移到 DL_Inactive 状态, 但是不能迁移到 DL_Init 状态。 这也意味着数据链路层从 DL_Active 状态迁移出去后, 必须重新进行对端设备的识别和流量控制初始化, 之后才能进入 DL_Active 状态。
在多数情况下, 数据链路层从 DL_Active 状态迁移到 DL_Inactive 状态时, 意味着处理器 系统出现了异常, 系统软件需要处理这些异常。 但是在下列情况时, 数据链路层状态从 DL_Active 状态迁移到 DL_Inactive 状态时并不会引发异常。
- Bridge Control Register 的 Secondary Bus Reset 位被系统软件置为 1 时, 数据链路层将迁 移到 DL_Inactive 状态。
- Link Disable 位被系统软件置为 1 时, 数据链路层迁移到 DL_Inactive 状态。
- 当一个 PCIe 端口向对端设备发送 “PME_Turn_Off” 消息之后, 其数据链路层经过一段时间, 可以迁移到 DL_Inactive 状态。 RC 和 Switch 在进入低功耗状态之前, 将向其下 游端口广播 PME_Turn_Off 消息, 下游 PCIe 设备收到该消息后, 将向 RC 和 Switch 发出 PME_TO_Ack 回应。 当 RC 和 Switch 的下游端口收到这个回应报文后, 数据链路层可以迁移到 DL_Inactive 状态。
- 如果 PCIe 链路连接了一个支持 “热插拔” 功能的 PCIe 插槽, 而当这个插槽的 Slot Capability 寄存器的 “Hot Plug Surprise” 位为 1 时, 数据链路层将迁移到 DL_Inactive 状态。
- 如果 PCIe 链路连接一个热插拔插槽, 当这个插槽的 Slot Control 寄存器的 “Power Controller Control” 位为 1 时, 数据链路层也将迁移到 DL_Inactive 状态。
7. 1. 2 事务层如何处理 DL_Down 和 DL_Up 状态
当事务层收到数据链路层的 DL_Down 状态信息时, 表示出现了以下情况。
- PCIe 链路的对端没有连接设备。
- PCIe 链路丢失了与对端设备的连接。
- 数据链路层和物理层出现某种错误, PCIe 链路不能正常工作。
- 系统软件禁用 PCIe 链路。
当事务层收到 DL_Down 状态信息后, 将不再从数据链路层中接收 TLP, 除非是数据链 路层已经使用 ACK / NAK 报文确认过的 TLP。 这些被确认过的 TLP 已经被数据链路层接收完 毕, 因此事务层可以接收这些 TLP。
7. 1. 3 DLLP 的格式
DLLP 与 TLP 的概念并不相同, DLLP 产生于数据链路层, 终止于数据链路层, 这些报 文不会出现在事务层中, 而且对系统软件透明。 设置 DLLP 的目的是为了保证 TLP 的正确传送和管理 PCIe 链路。
值得注意的是, DLLP 并不是由 TLP 加上 Sequence 前缀和 LCRC 后缀组成的, 而具有单独的格式。 一个 DLLP 由 6 个字节组成, 其中第 1 个字节存放 DLLP 的类型, 第 2 ~ 4 个字节存放的数据与 DLLP 类型相关, 而最后两个字节存放当前 DLLP 的 CRC 校验。 DLLP 的格式 如图 7-4 所示。
大多数 DLLP 由 PCIe 设备自动产生, 而与事务层没有直接联系。 PCIe 总线定义了以下几类 DLLP 报文, 如表 7-1 所示。
这些 DLLP 报文的描述如下。
- ACK DLLP。 该 DLLP 由接收端发向发送端。 接收端收到 TLP 报文后, 将根据数据链 路层的阈值设置, 向对端设备发送 ACK DLLP, 而不是每接收到一个 TLP, 都向对端 发送一个 ACK DLLP。 该 DLLP 表示接收端正确收到来自对端的 TLP。
- NAK DLLP。 该 DLLP 由接收端发向发送端。 该 DLLP 表示接收端有哪些 TLP 没有被正 确接收, 发送端收到 NAK DLLP 后, 将重传没有被正确接收的 TLP, 同时释放已经被 正确接收的 TLP。 ACK 和 NAK DLLP 与 ACK / NAK 协议相关, 是数据链路层的两个重 要 DLLP。 这两个 DLLP 的详细作用如第 7. 2 节所述。
- Power Management DLLPs。 PCIe 设备使用该组 DLLP 进行电源管理, 并向对端设备通 知当前 PCIe 链路的状态。 PCIe 总线还定义了一组与电源管理相关的 TLP, 这些 TLP 与这组 DLLPs 有一定的联系, 但是其作用并不相同。 PCIe 总线使用该组 DLLP 保证电 源管理状态机的正确运行。
- Flow Control Packet DLLPs。 该组 DLLP 包括 InitFC1、 InitFC2、 UpdateFC DLLP, PCIe 总线使用这些 DLLPs 进行流量控制。 在 PCIe 总线中, 数据传送由三大类组成, 分别 为 Posted、 Non-Posted 和 Completion。 这三种数据传送方式有些细微区别, PCIe 设备 为这三种数据传送设置了不同的数据缓冲。 流量控制是 PCIe 总线的一个重要特性, 第 9 章将重点介绍这些内容。
- Vendor-specific DLLP。 一些定制的 DLLP, PCIe 总线规范并未对此约束。 这些 DLLP 由 用户自定义使用。
本节将重点介绍 ACK / NAK DLLP, 这两个 DLLP 与 PCIe 总线的 ACK / NAK 协议直接相 关。 在 PCIe 总线中, 数据链路层使用 ACK / NAK 协议保证 TLP 的可靠传送。 ACK / NAK DLLP 的格式如图 7-5 所示。
ACK / NAK DLLP 各字段的详细说明如表 7-2 所示。
发送端的数据链路层负责将 TLP 传送给接收端, 而接收端的数据链路层在收到 TLP 之后, 将向发送端发送 ACK / NAK DLLP。 发送端和接收端通过某种传送协议, 完成数据链路 层的数据交换, 在 PCIe 总线中, 这个协议称为 ACK / NAK 协议。
7. 2 ACK / NAK 协议
ACK / NAK 协议是一种滑动窗口协议。 PCIe 设备的发送端和接收端分别设置了两个窗口。 发送端在发送 TLP 时, 首先将这个 TLP 放入发送窗口中 (这个窗口即 Replay Buffer) , 并对这些 TLP 从 0 ~ n 进行编号。 只要发送窗口不满, 发送端就可以持续地从事务层中接收 报文, 然后将其放入 Replay Buffer 中。
发送端需要保留在这个窗口中的数据报文, 并在收到来自接收端的 ACK / NAK 确认报文之后, 统一释放保存在发送窗口中的报文, 并滑动这个发送窗口。 当发送端收到接收端对第 n 个报文的确认后, 表示第 n、 n - 1、 n - 2 等在窗口中的报文都已经被正确收到, 然后统一 滑动这个窗口。 PCIe 总线使用这种方法可以提高窗口的利用率。
与此对应, 接收端也维护了一个窗口, 该窗口记录数据报文的发送序列号范围。 当数据 报文到达后, 如果其序列号在接收窗口范围内, 接收端将接收该报文, 并根据根据实际情 况, 向发送端发送回应报文。 这个回应报文包括 ACK 和 NAK DLLP。 下文将分别讨论发送 端和接收端如何使用 ACK / NAK 协议。
7. 2. 1 发送端如何使用 ACK / NAK 协议
数据链路层在发送 TLP 之前, 发送端首先需要将 TLP 进行封装, 加上 Sequence 前缀和 LCRC 后缀, 之后再将这个 TLP 放入 Replay Buffer 中。 发送端设置了一个 12 位的计数器 NEXT_TRANSMIT_SEQ, 这个计数器的初始值为 0, 当数据链路层处于 DL_Inactive 状态时, 该计数器将保持为 0。 为简化起见, 本节只讲述数据链路层处于 DL_Active 状态时的情况, 而不讲述处于 DL_Inactive 状态时的情况。
发送端使用计数器 NEXT_TRANSMIT_SEQ 的当前值设置 TLP 的 Sequence 号, 该计数器 的初始值为 0。 PCIe 设备每发送完毕一个 TLP, 这个计数器将加 1, 直到该计数器的值为 4095(NEXT_TRANSMIT_SEQ 的最大值) 。 当计数器的值为 4095 后, 再进行加 1 操作时, 该 计数器将回归为 0。 而 LCRC 是根据 TLP 的内容计算出来的, 用来保证数据传递的完整性, 本节不介绍 LCRC 的计算过程。 对此有兴趣的读者请参考 PCIe 总线规范。
与此对应, 接收端也设置了一个 12 位的计数器 NEXT_RCV_SEQ。 这个计数器记录接收 端即将接收的 TLP 的 Sequence 号。 这个计数器的初始值为0, 当数据链路层处于 DL_Inactive 状态时, 该计数器保持为 0。 在正常情况下, 到达接收端的 TLP, 其 Sequence 号和这个计数 器中的内容一致。 当接收端将这个 TLP 转发到事务层后, 这个计数器将加 1, 当计数器的值 为 4095 后, 再进行加 1 操作时, 该计数器将回归为 0。 如果到达接收端的 TLP, 其序号与这 个计数器中的值不一致时, 接收端需要进行特殊处理, 详见下文。
发送端为处理来自接收端的 ACK / NAK DLLP, 设置了一个 12 位的计数器 ACKD_SEQ。 这个计数器记载最近接收到的 ACK / NAK DLLP 的 AckNak_Seq_Num 字段。 这个计数器的初 始值为全 1, 当数据链路层处于 Inactive 状态时, 该计数器保持为全 1。 发送端收到 ACK / NAK DLLP 后, 将使用这些 DLLP 中的 AckNak_Seq_Num 字段更新 ACKD_SEQ 计数器。
如果(NEXT_TRANSMIT_SEQ - ACKD_SEQ) mod 4096 > = 2048 时, 发送端将不会从事务 层继续接收新的 TLP, 因为此时发送端已经发送了许多 TLP, 但是接收端可能并没有成功接 收这些 TLP, 因此并没有及时发送 ACK / NAK DLLP 作为回应。 在多数情况下, 当 PCIe 链路 出现了某些问题时, 才可能导致该公式成立。 此外 ACKD_SEQ 计数器还可以帮助发送端重 发错误的 TLP, 下文将详细解释这个功能。
发送端首先将从事务层获得的 TLP 存放到 Replay Buffer 中, 在 Relpay Buffer 中可以存放多个 TLP, 这个 Replay Buffer 为发送端使用的发送窗口。 PCIe 总线并没有规定在 Replay Buffer 中存放 TLP 的个数, 不同的设计可以采用的大小不同, 其中有一个重要的原则就是不 能使 Replay Buffer 成为整个设计的瓶颈, Replay Buffer 应该始终保证有足够的空间接收来自事务层的报文。
TLP 进入 Replay Buffer 之后, 发送端首先将这个 TLP 封装, 然后从 Replay Buffer 中发送到物理层, 最终达到接收端。 发送端将 TLP 发送出去之后, 将等待来自接收端的应答, 接收端使用 ACK / NAK DLLP 发送这个应答。 发送端根据应答结果决定是将 TLP 从 Replay Buffer 中清除, 还是重发在 Replay Buffer 中的 TLP。
7. 2. 2 接收端如何使用 ACK / NAK 协议
接收端首先从物理层获得 TLP, 此时在这个 TLP 中包含 Sequence 号前缀和 LCRC 后缀。 接收端收到这个 TLP 后, 首先将这个报文放入 Receive Buffer 中, 然后进行 CRC 检查。 如果 CRC 检查成功, 接收端将根据接收缓冲的阈值发送 ACK DLLP 给发送端, 并将这个 TLP 传给事务层。 除了 CRC 校验外, 接收端还需要做其他检查, 本节对此不进行介绍。
- 接收端发送 ACK DLLP
当接收端收到的 TLP 没有出现 LCRC 错误, 而且 TLP 的 Sequence 号和 NEXT_RCV_SEQ 计数器的值相同时, 接收端将正确接收这个 TLP, 并将其转发给事务层, 随后接收端将 NEXT_RCV_SEQ 计数器加 1。
接收端根据具体情况, 决定向发送端立即发送 ACK DLLP, 还是等待接收到更多的 TLP 后再发送 ACK DLLP。 如果接收端决定发送 ACK DLLP, 则该 ACK DLLP 的 AckNak_Seq_Num 字段为 NEXT_RCV_SEQ - 1, 即已经正确接收 TLP 的 Sequence 号。
接收端不会对每一个正确接收的 TLP 发出 ACK DLLP 回应, 因为这样将严重影响 PCIe 总线链路的使用效率, 而是收集一定数量的 TLP 后, 统一发出一个 ACK DLLP 回应表示之 前的 TLP 都已正确接收。
为此接收端使用了一个 ACKNAK_LATENCY_TIMER 计数器, 当这个计数器超时或者接 收的报文数超过一个阈值后, 向发送端发送一个 ACK DLLP 回应。 此时这个 ACK DLLP 的 AckNak_Seq_Num 字段为在这段时间以来, 最后一个被正确接收的 TLP 的 Sequence 号。 不同的设计在此处的实现不尽相同。 但是这些实现都要遵循以下两个原则。
- 接收端在收到一定数量的报文后, 统一发送一个 ACK DLLP 做为回应。
- 接收端收到的报文虽然没有到达阈值, 但是 ACKNAK_LATENCY_TIMER 计数器超时后, 仍然要发出 ACK DLLP 作为回应。 在某些情况下, 发送端可能在发送一个 TLP 后, 在很长一段时间内, 都不会发送新的 TLP, 此时接收端必须及时给出 ACK DLLP 回应, 以免发送端的 REPLAY_TIMER 计数器溢出。
7. 2. 3 数据链路层发送报文的顺序
数据链路层还规定了报文发送的顺序。 由上文的描述中, 我们可以发现 DLLP 和 TLP 的发送共用一个 PCIe 链路, 除此之外物理层的报文 PLP (Physical Layer Packet) 也使用同样的链路。 因此 PCIe 链路需要合理地安排报文的发送顺序, 以避免死锁。 其发送顺序如下所示。
- 正在发送的 TLP 或者 DLLP 具有最高的优先权。 PCIe 总线为了保证数据的完整性, 不允许打断正在传送的报文。 从理论上讲, 打断正在传送的报文是可行的, 但是硬件需要更大的代价, 也需要制定更加复杂的协议保证数据的完整性。
- PLP 的传送。 一般来说, 处于协议底层的报文优先权高于处于协议高层的报文, 这也是解决死锁的一个有效方法。
- NAK DLLP。 NAK DLLP 需要优先于 TLP 的发送, 原理同上。
- ACK DLLP。 ACK DLLP 响应正确接收的报文, 在绝大多数处理过程中, 错误处理报文优先于正确的响应, 这也是一种防止死锁的方法。
- 重新传送 Replay Buffer 中的 TLP。 也是一种发现错误后的恢复手段, 因此这种报文的传递优先权高于其他 TLP。 因为在错误没有处理完毕之前, 其他 TLP 的传递是没有意义 的, 接收端都将丢弃这些报文。
- 其他在事务层等待的 TLP。
- 其他 DLLP, 这些 DLLP 包括地址路由, 电源管理等报文, 这些报文与数据报文的传递无关, 是 PCIe 总线规定的一些控制报文, 所以优先权最低。
7. 3 物理层简介
如图 4-4 所示, 物理层在数据链路层和 PCIe 链路之间, 其主要作用有两个, 一是发送数据链路层的 TLP 和 DLLP; 二是发送和接收在物理层产生的报文 PLP (Physical Layer Packet) ; 三是从 PCIe 链路接收数据报文并传送到数据链路层。
物理层主要由物理层逻辑模块和物理层电气模块组成, 本节主要介绍物理层的逻辑模 块, 包括 8 / 10b 编码、 链路训练等一些最基础的内容, 并通过介绍差分信号的工作原理, 简 要介绍物理层的电气模块。 物理层的电气模块对于深入理解 PCIe 总线规范非常重要, 但是 许多系统软件工程师因为缺少必要的基础知识, 很难理解这部分内容。
物理层的电气模块与差分信号的工作原理密切相关, 这部分原理包括一系列与信号完整 性相关的课题。 而信号完整性本身就是一个专门的话题, 其难度与复杂程度较高。 信号完整 性所追求的目标如下。
- 保证发送的信号可以被接收端正确接收。
- 保证发送的信号不会影响其他信号。
- 保证发送的信号不会损坏接收器件。
- 保证发送的信号不会产生较大的 EMI 电磁噪声。
7. 3. 1 PCIe 链路的差分信号
PCIe 链路使用差分信号进行数据传递, 而差分信号由两个信号组成, 这与 PCI 总线使 用的单端信号有较大区别。
与单端信号相比, 差分信号具有许多优势。
- 抗干扰能力较强。 差分信号不受 SSO 噪声的影响, 其走线是等长的, 且距离较近。 当外界存在噪声干扰时, 这些干扰同时被耦合到两个信号上, 因为接收端只关心这两个信号 的差值, 这些干扰相减后可以忽略不计。
- 能够有效抑制信号传递带来的 EMI 干扰。 差分信号的极性相反, 对外界辐射的电 磁场可以相互抵消, 因此产生的噪声较小。
- 逻辑状态定位准确。 由于差分信号的开关变化位于两个信号的交点, 而不像单端信 号使用高低电压两个阈值进行判断, 因而受制造工艺、 外部环境变化的影响较小, 使用差分 信号时, 接收逻辑较易判断逻辑状态 “0” 和 “1” 。
- 提供的数据带宽较高。 由于差分信号受外界环境的影响较小, 能够运行在更高的时 钟频率上, 从而提供的数据带宽较高。
7. 3. 2 物理层的组成结构
PCIe 总线的物理层通过 LTSSM 状态机对 PCIe 链路进行配置与管理, 并与数据链路层进行数据交换, 由逻辑子层 (Logical Sub⁃block) 和电气子层 ( Electrical Sub⁃block) 组成。 本 节主要讲述逻辑子层。 逻辑子层与数据链路层进行数据交换, 由发送逻辑 TX 和接收逻辑 RX 组成, 其结构如图 7-13 所示。
如上图所示, 物理层发送报文的过程如下。
- 物理层从数据链路层获得 TLP 或者 DLLP, 然后放入 Tx Buffer 中。
- 物理层将这些 TLP 或者 DLLP 加入物理层的前缀 ( Start Code ) 和后缀 ( End Code) , 后通过多路选择器 Mux, 进入 Byte Stripping 部件。 物理层也定义了一系列 PLP, 这 些 PLP 也可以通过 Mux, 进入 Byte Stripping 部件。
- PCIe 链路可能由多个 Lane 组成, Byte Stripping 部件可以将数据报文分发到不同的 Lane 中。 在 PCIe 链路的不同 Lane 中传递的数据可能存在漂移, 即 Skew, Byte Stripping 部件还有一个重要功能即消除这个漂移, 即 De-skew。
- 数据进入到各自 Lane 的加扰 ( Scrambler) 部件, “加扰” 后进行 8 / 10b 编码, 最后通过并转串逻辑将数据发送到 PCIe 链路中。
物理层的接收过程是发送的逆过程, 其步骤如下。
- 物理层从 PCIe 链路的各个 Lane 获得串行数据, 并通过 8 / 10b 解码和 De⁃Scrambler 部件, 发送到 “Byte Un⁃Stripping” 部件。
- “Byte Un⁃Stripping” 部件将来自不同 Lane 的数据合并, 进行 De⁃skew 操作, 然后取 出物理层的前后缀并进行边界检查后, 将数据放入 Rx Buffer 中。
- 物理层将在 Rx Buffer 中的数据传递到数据链路层。
物理层的数据在通过 Byte Un⁃Stripping / Stripping 部件时, 需要注意大小端模式的转换。 而 Scrambler 和 De⁃Scrambler 部件的主要作用是对数据流进行 “加扰” 和 “解扰” 操作。 在 串行链路上进行数据传递时, 如果在字符流中存在某些规律, 这些 “规律” 将会叠加, 并 产生较大的 EMI (Electromagnetic interference) 噪声。
Scrambler 部件的主要作用就是通过 “加扰” 的方法削减 EMI 噪声, 所谓加扰是指将源数据流与一个随机序列进行异或操作后, 再发送出去。 此时被发送出的数据流也基本是伪随机的, 从而降低了发送数据时产生的 EMI 噪声。
☆