PCIe(三)TLP数据包格式
在PCIe协议中,有三类数据包。分别是事务层的TLP(Transaction Layer Packet)、数据链路层的DLLP(Data Link Layer Packet)和 物理层的Ordered Set(有序集)。简单介绍一下Ordered Set, 不同于TLP/DLLP,他没有起始/结束字符,直接由物理层解析,不通过事务层或数据链路层,仅在相邻设备的物理层之间传输,主要用于链路初始化、训
在PCIe协议中,有三类数据包。分别是事务层的TLP(Transaction Layer Packet)、数据链路层的DLLP(Data Link Layer Packet)和 物理层的Ordered Set(有序集)。简单介绍一下Ordered Set, 不同于TLP/DLLP,他没有起始/结束字符,直接由物理层解析,不通过事务层或数据链路层,仅在相邻设备的物理层之间传输,主要用于链路初始化、训练和电源管理等关键功能等。
TLP是PCIe架构在设备之间传输数据和信息的基本单元,他从发起方的device core产生,经过Data Link和Physical层的封装,接收方Physical/Data Link层的解封装,到达接收方的事务层,给到device core。
具体而言,Device Core发起请求(如内存读写、配置访问等),将请求信息(地址、数据类型、数据负载等)传递给Transaction Layer。Transaction Layer根据请求生成TLP,包括:Header(头部):包含路由信息(地址或ID)、事务类型(Memory Read/Write、Configuration等)、数据长度等; Playload(可选数据负载):如写请求携带的数据; ECRC(End-to-End CRC):用于端到端数据完整性校验。然后TLP将被放入虚拟通道(Virtual Channel)缓冲区,等待流控信用(Flow Control Credits)确认后,传递给Data Link Layer。
到达data layer后,会被添加序列号(Sequence Number)和链路CRC(LCRC),用于链路级错误检测和重传。 经此处理后的TLP会被创建一个副本,保存在重放缓冲区(Retry Buffer)中,这是为了应对传输出错的情况。
到达Physical Layer后,如果是Gen1/2则会被添加起始(STP/SDP)和结束(END/EDB)控制符号,标识TLP边界,如果是Gen3则会用更高效的帧结构,添加STP令牌在首端,通过STP中包含的TLP大小信息来确定TLP边界。除此之外,如果是多链路串行传输,则需要对TLP进行字节分割(Byte Striping)。进行编码(如8b/10b或128b/130b),最终通过差分信号发送到PCIe链路。
在接受方一侧,Physical Layer 进行串并转换,解码,字节反条带化,移除控制符号。然后Data Link Layer检查LCRC和序列号,若正确,发送Ack DLLP确认,移除LCRC和序列号,将TLP上传至Transaction Layer。若错误,发送Nak DLLP触发发送方重传。Transaction Layer解析TLP头部,根据路由信息决定:接受(若目标为本设备):检查ECRC(若存在),将数据/请求传递给设备核心。转发(若为Switch/RC):根据路由规则(地址/ID/隐式路由)将TLP发送到下一跳链路。
一、TLP结构
TLP主要包括三个部分:Header, Data Payload和ECRC(End to End CRC)。还有可选的TLP前缀(TLP Prefixes)和TLP摘要(TLP Digest)。
下面进行详细解释。
1.1 Header
如下所示是一个4DW(双字)的header格式。Header的大小通常为3DW(12字节)或4DW(16字节)
下面介绍一些重要的字段。
1.1.1Fmt和Type字段
其中最为重要的是Fmt和Type字段,可以通过他们来判断TLP事务类型。
1.1.2 Digest字段
Digest字段主要用于标识是否需要在TLP上添加ECRC,位于TLP尾部(如果选中),ECRC全称为End-to-End Cyclic Redundancy Check,即端到端循环冗余校验。它用于检测从TLP的发送端(Requester)到最终接收端(Completer)整个路径中的数据完整性错误。覆盖TLP中不变的字段(如Header、Data Payload等),确保在传输过程中未被篡改。
这里需要与LCRC区分一下,ECRC是由发送端的Transaction Layer生成,接收端的Transaction Layer校验,检测 从 TLP 源头到最终接收者 之间的整个路径是否发生了错误。而LCRC由Data Link Layer添加,紧随Sequence Number后在TLP尾部。它用于检测单条PCIe链路上(相邻设备之间)TLP传输过程中的错误。 覆盖整个TLP(包括Header、Data Payload、ECRC及Sequence Number)。它是检测 当前链路(一个 hop,比如 Root Complex 到 Switch、或 Switch 到 Endpoint)之间的物理传输是否出错。举个例子。A向B寄包裹。ECRC就好比B向A确认,包裹里面的东西是不是对的,是否有掉包或者损坏。LCRC就好比A把包裹给快递员后,快递员先检查一下是否破损,快递员把包裹给到物流中心后(switch), 物流中心检查快递是否正确。物流中心把快递分派到寄件员,该寄件员工再次确认包裹。因此,同一个LCRC是不会跨Switch/Bridge的,而是会在 Switch/Bridge 的出口端口(egress port)重新计算一个 LCRC,然后再转发给下一级链路。而ECRC是可以跨Switch/Bridge的,并且整个过程中值不会改变。
值得注意的是,ECRC 是用来检测而不是防止篡改的!!!ECRC 无法抵御恶意攻击或主动篡改。ECRC 是对完整的 TLP(包括 Header 和 Payload)计算的 CRC32 值,因此如果篡改了data或者header,那么接收端(completion)就会发现重新计算出的ECRC值不等于TLP中的ECRC值,最终就确定TLP被篡改或者损坏了。
我们说ECRC是为了保证TLP中不变的字段(如Header、Data Payload等传输过程中未被篡改,那说明还有可变字段,TLP中有两个可变字段,即Type 字段的 Bit 0和EP(Error/Poisoned)bit,这两比特的改变不会让ECRC检测误认为TLP被篡改。
1.2.3 EP字段(Error/Poisoned)
位于header中的位于Byte 2的Bit 6),用于指示伴随的数据是否被损坏或者修改(即数据中毒)。当EP=1时,表示数据无效,但事务允许正常完成(称为数据中毒)。
1.2.4 Type 字段的Bit 0
当配置请求从上游向下游传递时,若目标总线为桥的次级总线(Secondary Bus),桥会将Type字段的Bit 0从1改为0,表示将Type 1请求转换为Type 0请求。
当一个配置事务从 Root Complex 发出,想要访问桥后面的设备(不在直连下一级)时,RC会发出 Type 1 配置请求,PCIe Bridge 收到 Type 1 请求后判断是否要转发,主要是判断目标的 Bus Number 是否匹配自己的 Secondary/Subordinate Bus,如果是发往 Bridge 下级的设备,转发该请求时,自动将 Type 字段从 Type 1 ➜ Type 0。
简言之,Type0是配置请求发往下一级直接连接的设备,如RC-Endpoint, 末端是Endpoint。Type1是配置请求发往下级的 PCIe Bridge 或 Switch,如RC ➜ Bridge,必须要穿越Bridge或者Switch。如果RC直接与Endpoint相连那Type就为0,通过Bridge/Switch与Endpoint相连那就是先1后0。
1.2.5 Last/First Byte Enable
在 PCIe 中,Byte Enable(BE)字段 是一个非常重要的机制,用于描述在数据传输中哪些字节是有效的、要被读或写。这个机制来自 PCI 总线传统设计,在 PCIe 中依然保留。PCIe是双字DW对齐的,如果一个数据包大小或者起始/结束地址不是DW对齐,那么就需要使用首 DW 字节使能(First DW Byte Enable)和尾 DW 字节使能(Last DW Byte Enable)来进行对齐。
这两个信号是高位有效的,比如First DW BE = 1100表示第一个 DWORD 中,只有 Byte 2 和 Byte 3 有效(即高两个字节)。
举个例子,如果要访问地址 0x1003 开始的 5 字节数据,Last DW BE[3:0] 和First DW BE[3:0] 应该怎么设定呢?
首先每个 DWORD 边界:
0x1000 ~ 0x1003 → 第一个 DWORD
0x1004 ~ 0x1007 → 第二个 DWORD
所以第一个 DWORD只用到 Byte 3,第二个 DWORD:用到 Byte 0, 1, 2, 3(4 个字节)
First BE = 1000,Last BE =1111
使用Byte Enable时需要参考以下原则:
高位有效:BE 的 bit3 对应 Byte3,bit0 对应 Byte0,1 表示有效,0 表示无效;
无效字节不得被 Completer 使用:BE = 0 的字节视为无效,不应被处理;
仅在单个 DW 内有效时,Last DW BE 必须为 0000b;
Length > 1 DW 时,First DW BE 至少要有 1 个 bit 为 1(必须有有效数据起点);
Length ≥ 3 DW 时,First/Last DW BE 必须为连续的 1(用于偏移表示);
仅当 Length = 1 DW 时,才允许 First DW BE 中 非连续的 1(如 1010)合法;
仅当 Length = 1–2 DW 时,First 和 Last DW BE 才能为 非连续的 1;
Write 请求中 BE 全为 0(Length = 1):合法,Completer 不处理任何数据;
Read 请求中 BE 全为 0(Length = 1):返回 1 DW 的 未定义数据;
Read BE 全 0 可用于刷新机制:利用 Completion 顺序,实现 posted write 的刷新屏障。
带有数据荷载的 TLP 的一些额外规则:
Data Payload的大小由TLP Header中的Length决定;
Data Payload的数据采用的是Little Endian,即低字节存放于低地址中;
Data Payload的大小并不是有效的数据的大小,有效数据的大小是由Data Payload和Byte Enable共同决定的;
当TLP类型为Message时,Length一般是保留的(Reserved),除非该Message是带有数据的(MsgD);
TLP的Data Payload大小不得超过Max_Payload_Size的值,该值位于Device Control ;Register中。对于比较大的数据量,因此只能分多次进行发送。对于读请求来说,并没有Data Payload,也就是说该规则并不适用于读请求;
需要特别注意的是,起始地址和结束地址之间不能够跨越4KB的地址边界。
1.2 TLP类型
主要可以分为IO Request,Memory Requests,Configuration Requests,Completions,以及Message Request五大类。具体由Fmt和Type共同决定,参看前面的那张表格
1.2.1 IO Request
用于访问 legacy I/O 空间。它是在兼容传统 PCI 设备或系统中保留的机制,在现代 PCIe 系统中已极少使用,但仍属于协议定义的一部分。在现代 PCIe 设计中,推荐全部使用 Memory Request(MMIO)替代。
1.2.2 Memory Requests
Memory Requests 是最常用、最核心的一类 TLP(Transaction Layer Packet),用于读写设备或主内存中的地址空间。
值得注意的是,Payload 不可跨越 4KB(1024DW) 边界,若长度跨 4KB,需拆分成多个 TLP,并且length字段必须大于等于1, 所有的内存映射写(Memory-Mapped Write)都是 Posted 的, 以此提升速度。
1.2.3 Configuration Requests
主要是对Type0和Type1的配置,前面已经提到过,不再赘述。
1.2.4 Completions
由Header中的Compl. Status[2:0]标识。
1.2.5 Message Requests
这个字段包含的标识码表示发送的 Message 是什么种类。
0000 0000b=解锁 Message(Unlock Message)
0001 0000b=延迟容忍报告(Lat. Tolerance Reporting)
0001 0010b=优化的 Buffer 刷新/填满(Optimized Buffer Flush/Fill)
0001 xxxxb=电源管理 Message(Power Mgt. Message)
0010 0xxxb=INTx Message
0011 00xxb=错误 Message(Error Message)
0100 xxxxb=忽略 Message(Ignored Message)
0101 0000b=设置插槽功耗 Message(Set Slot Power Message)
0111 111xb=厂商定义的 Message(Vendor Defined Message)
###声明:图片和内容大多来自于PCIe官方文档和自己思考,少量参考其他博主文章,如有侵权请联系删除。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)