TLP数据包原理学习
PCIe硬件协议
PCIe的连接是创建在一个单向的序列的(1-bit)点对点连接基础之上,这称之为通道(lane)。而早期PCI连接基于总线控制,所有设备共享双向32位并行总线。
PCIe是一个多层协议,由事务层,数据交换层和物理层构成。物理层又可进一步分为逻辑子层和电气子层;逻辑子层又可分为物理代码子层(PCS)和介质接入控制子层(MAC)。PCIe通过ASPM协议进行电源管理。
数据链路层采用按序的交换层信息包(Transaction Layer Packets,TLPs),是由交换层生成,按32位循环冗余校验码(CRC,本文中用LCRC)进行数据保护,采用著名的协议(Ack and Nak signaling)的信息包。能通过LCRC校验和连续性校验的TLPs的过程被称为Ack(命令正确应答);没有通过校验的称为Nak(没有应答)。没有应答的TLPs或者等待超时的TLPs会被重新传输。这些内容存储在数据链路层的缓存内,以确保TLPs的传输不受电子噪音干扰。PCIe定义ACK在收到TLP数据包之后,在一定时间内必须回应ACK,也就是ACK延迟(ACK Latency)的等待时间。因应ACK/NAK流程的需要,必须实现出重新播送缓冲器(Replay Buffer)。
TLPs加上Sequence number以及16 比特 CRC 后,被包裹成为数据链路层信息包(Data Link Layer Packet,DLLP),除了资料传递外,交握信号ACK和NAK信号亦被包裹在信息包中发送,除此之外也用来传送两个互连设备的交换层之间的流控制信息和实现电源管理功能。
TLP的大小限制是在外设的配置阶段(peripheral’s configuration stage)设置的,通常设置每个TLP的最大值为128、256或521字节。
TLP数据包格式
TLP格式定义如下:
- TLP 前缀(可选)
- TLP头
- 存在或不存在数据的payload
- TLP摘要(可选)
TLP头
TLP头定义数据含义如下:
- Fmt:数据包格式定义
- Type:数据包类型
- R:Reserved,为0
- TC:Traffic Class,流量优先级,0-7,数值越大优先级越高
- Attr:Attrbiute, 属性
- TH: TLP Processing Hints
- TD: TLP Digest
- EP:End Point(TLP is Normal or Poisoned)
- AT:Address Translation
- Length:Payload数据长度
数据包Format和Type编码表:
TLP Type | Format | Type | Description | Structure | Hexadecimal |
---|---|---|---|---|---|
MR | 000 | 0 0000 | Memory Read Request | TLP_TYPE_MRd32 | 0x00 |
001 | 0 0000 | TLP_TYPE_MRd64 | 0x20 | ||
MRL | 000 | 0 0001 | Memory Read Request Locked | TLP_TYPE_MRdLk32 | 0x01 |
001 | 0 0001 | TLP_TYPE_MRdLk64 | 0x21 | ||
MW | 010 | 0 0000 | Memory Write Request | TLP_TYPE_MWr32 | 0x40 |
011 | 0 0000 | TLP_TYPE_MWr64 | 0x60 | ||
IOR | 000 | 0 0010 | I / O Read Request | TLP_TYPE_IORd | 0x2 |
IOW | 010 | 0 0010 | I / O Write Request | TLP_TYPE_IOWr | 0x42 |
CR0 | 000 | 0 0100 | Configuration Read Type 0 | TLP_TYPE_CfgRd0 | 0x4 |
CW0 | 010 | 0 0100 | Configuration Write Type 0 | TLP_TYPE_CfgWr0 | 0x44 |
CR1 | 000 | 0 0101 | Configuration Read Type 1 | TLP_TYPE_CfgRd1 | 0x5 |
CW1 | 010 | 0 0101 | Configuration Write Type 1 | TLP_TYPE_CfgWr1 | 0x45 |
Msg | 001 | 1 0 r2 r1 r0 | Message Request | ||
MsgD | 011 | 1 0 r2 r1 r0 | Message Request with Data | ||
Cpl | 000 | 0 1010 | Completion without data (0 Bytes) | TLP_TYPE_Cpl | 0xA |
CplD | 010 | 0 1010 | Completion with data (data will be present in TLP) | TLP_TYPE_CplD | 0x4A |
CplLk | 000 | 0 1011 | Completion for Locked Memory read without data | TLP_TYPE_CplLk | 0xB |
CplDLk | 010 | 0 1011 | Completion for Locked Memory Read | TLP_TYPE_CplLkD | 0x4B |
FetchAdd | 010 / 011 | 0 1100 | Fetch and Add atomic Operation Request | ||
Swap | 010 / 011 | 0 1101 | Unconditional Swap Atomic Operation Request | ||
CAS (Compare and Swap) | 010 / 011 | 0 1110 | Compare and swap Atomic Operation | ||
LPrfx | 100 | 0 L3 L2 L1 L0 | Local TLP Prefix | ||
EPrfx | 100 | 1 E3 E2 E1 E0 | End-End TLP Prefix |
TLP长度编码:
Length [9:0] | TLP Data Payload Size |
---|---|
00 0000 0001 | 1 DWord |
00 0000 0010 | 2 DWord |
…………………. | ………………… |
11 1111 1111 | 1023 DWord |
00 0000 0000 | 1024 DWord |
假设有1024个DWord的数据,那么Root Complex(芯片组)将把数据分为更小的数据包,然后发送到EndPoints。
如果中间需要经过Switch,则数据不会被拆分,直接原样转发。
TLP写入请求数据包示例
假设CPU使用32位寻址,将值 0x12345678写入物理地址 0xfdaff040,数据包由四个32位字(4个DW)组成,如下图所示:
数据包被传输为0x40000001, 0x0000000f, 0xfdaff040, 0x12345678。
- fmt&Type字段标识Memory Write Request: 0x4
- TD位为零,表示TLP Digest上没有额外的CRC,因为数据链路层上已经有自己的CRC来确保数据完整性了。
- Length字段为0x001,表示该TLP有一个DW的数据(32-bit word)
- 1st BE(1st Double-Word Byte Enable)字段标识第一个DW数据的四个字节中哪一个是有效的。示例中设置为0xf,表示四个字节都会被写入。
- Address字段只写入第一个DW数据的地址,即该地址的31-2 bits,TLP中DW 2的两个LSB为0,所以DW 2实际上读取的是写地址本身。将0x3f6bfc10 * 4,得到0xfdaff040。
- 最后为一个DW数据,PCIe通常为大端序。
TLP读取数据包示例
当CPU要从外设读取数据时,需要通过两个数据包:一个是从CPU到外设的TLP,请求外设执行读操作;另一个是从TLP返回的数据包。在PCIe术语中,将其称为Requester(例中为CPU)和Completer(外设)。
Read Request TLP
假设CPU需要地址0xfdaff040上的单个DW(32bit word),和写入请求一样,它可能需要在与内存控制器共享的总线(包括Root Complex)上初始化读取操作,然后生成要通过PCIe总线发送的TLP。
数据包由3个DW组成:0x00000001, 0x00000c0f, 0xfdaff040。它告诉外设在地址0xfdaff040处读取一个完整的DW,并将结果返回给ID为0x0000的总线实体。
它的基本结构和写入请求相似:
- fmt&Type字段标识Memory Read Request:0x00
- Requester ID字段标识此数据包的发送者ID为0,该值可以指示Completer将相应发送到何处。
- Tag字段值在读取请求包中很重要,当Completer响应时,它必须将此值复制到Completion TLP中。由于Completion answers来自总线上单个设备的多个请求,所以需要Requester将Completion answers与请求进行匹配。Tag字段值由Requester根据自己的需要进行设置,只需保证所有未完成请求的Tag是唯一的即可。虽然Tag字段分配了8个字节,但只允许使用5个LSB,一对总线实体之间最多有32个未完成的请求。但对于需要它的应用程序,标准内扩展可能允许多达2048个。
- Length字段表示应该读取几个DW。
- Address字段表示从哪个地址读取。
- 两个BE字段含义与写入请求包相同,只是设置的是需要读取的字节。
Completion TLP
当外设收到读取请求TLP时,它必须响应Completion TLP,即使无法完成请求的操作。下例为外设从内部资源读取数据块,并将结果返回给Requester的Completion TLP:
数据包由0x4a000001, 0x01000004, 0x00000c00, 0x12345678四个DW组成。其含义为“告诉总线实体0x0000,它对实体0x0100的请求的结果为0x12345678,Tag为0x0c”。之后CPU(或内存控制器=Root Complex)就可以在其内部记录中查找该请求的内容,并完成相关的总线周期。
- fmt&Type字段标识Completion with data
- Length字段值为0x001,表示该TLP有一个DW的数据(32-bit word)。由于TLP的长度是有限制的,可能会小于请求的DW数量,这种情况下会传回多个Completion TLP,所以需要在Length字段中标识该TLP有多少个DW。
- Byte Count字段,在单个Completion TLP的情况下,它标识数据包中payload的字节数。由于请求中的1st DW BE字段为1,所以我们有4个有效字节。但在多个Completion TLP的情况下,该字段定义需要传输的字节数,包括当前数据包中的字节。
- Lower Address字段,表示地址的7个最低有效位,从这里读取TLP中的第一个字节。上例中为0x40,即0xfdaff040的低位。该字段可用于多个Completion TLP。
- Completer ID标识此数据包的发送者ID,即0x0100。
- Requester ID标识此数据包的接受者ID,即0x0000(Root Complex)。如果有些PCIe switch需要路由,该字段将用于标识目标地址。
- Status字段为0x00,表示Completion成功。
- BCM字段始终为0,除非数据包来自具有PCI-X的网桥。
32位和64位寻址
读取或写入请求中的地址可以为32位或64位,TLP头长度也相应为3或4个DW。PCIe规范中提到,仅在必要时才使用4DW的TLP头:
针对4GB以下的地址,Requester必须使用32位格式。如果接收到地址低于4GB的64位格式请求(即地址的高32位全为0),则不会指定接收器的行为。
实际上,很少有外设的寄存器会映射超出4GB的范围,但是,DMA缓冲区很可能会超出4GB,所以在设计时一般都会支持64位寻址的读写TLP。
总线控制(DMA)
总线上的任何设备都可以像Root Complex一样在总线上发送读写TLP,这就允许外设直接访问CPU的内存(DMA)或者和对等外设交换TLP。
参考
https://www.semisaga.com/2019/07/pcie-tlp-header-packet-formats-address.html
http://xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-1
近期评论