PCIe学习(二)

文摘   科技   2024-09-07 09:15   北京  

2 事务层

事务层需要完成的事情:

  • TLP的生成(发送)和解析(接收)

  • TLP排序机制

  • 基于信用值的流量控制机制

  • 数据中毒(Data Poisoning)和ECRC完整性检测

事务层向上要与应用层通信,不同设备的应用层天差地别,在做芯片设计时要根据具体情况而定。PCIe规范没有对应用层提出约定。

2.1 地址空间

PCIe协议中有四类地址空间,对应四种请求事务,如下表:

Address Space

Transaction Types

Basic Usage

Memory

Read/Write

数据传输到/从(to/from)存储映射位置

I/O

Read/Write

数据传输到/从(to/from)I/O映射位置

Configuration

Read/Write

Function配置

Message

Baseline

从事件信令机制到通用消息传递

前三种是PCI协议中已有的事务类型,最后一种是PCIe协议中增加的事务类型。

2.1.1 存储事务

存储事务最常用,支持32-bit和64-bit两种地址格式,包括以下这些事务:

  • 读取请求,读取完成(Read Request/Completion)

  • 写入请求(Write Request)

  • 原子请求,原子完成(Atomic Request/Completion)


2.1.2 I/O事务

I/O事务是PCI时代的产物,PCIe规范中支持I/O事务只是因为需要兼容已有的PCI设备。PCIe原生设备本身不再支持I/O事务,既不会发出I/O请求,也不会响应主机的I/O请求。

I/O事务仅支持32-bit地址格式,包括以下这些事务:

  • 读取请求,读取完成(Read Request/Completion)

  • 写入请求,写入完成(Write Request/Completion)

在后续的学习中,可以暂时忽略I/O事务。

2.1.3 配置事务

配置事务是主机(也就是RC)用来配置Function,访问Function配置寄存器。在系统平台上电后,所有的PCIe链路自动完成链路训练(Link Training);随后主机软件(不区分固件/设备驱动/操作系统,统称为软件)对PCIe拓扑结构进行探索,已发现所有的可用设备,并分配唯一标识ID,这一过程称为“枚举(Enumeration)”;必要时,主机软件需要通过配置事务来请求PCIe设备开启或禁用某些功能,配置设备参数等等。

配置事务包括以下这些事务:

  • 读取请求,读取完成(Read Request/Completion)

  • 写入请求,写入完成(Write Request/Completion)

只有RC才有权限发出配置请求事务;EP或者Switch只可以接收配置请求事务,不允许发出配置请求。

2.1.4 消息事务

PCI时代有大量的引脚信号用于设备通信,这些引脚信号称作“边带(Side-Band)”信号。PCIe协议为了简化接口的引脚,将这些边带信号全部拿掉,设备间需要靠“带内(In-Band)”消息来通信。

消息事务是PCIe规范中新增加的事务类型,也就是说PCI设备不支持此类事务,如果系统中有PCI设备,则需要PCIe-PCI桥接设备做转换。

除去PCIe规范定义的消息类型,还支持厂商自定义消息,需要厂商自行定义和实现,并在其驱动软件中加以支持。

2.2 事务类型

上面是从地址空间出发,对PCIe事务进行了分类。接下来我们从事务发送和完成的角度看一下这些事务。

先区分几个概念。事务的发起者叫做请求者(Requester),事务的响应者叫做完成者(Completer)。有些事务需要完成者给请求者一个完成响应(Completion),将事务完成的状态通知请求者;而有些事务则不需要完成者返回完成响应,请求者发出事务以后即认为事务已经完成。不需要完成响应的事务是Posted类型,需要完成响应的事务是Non-Posted类型。所谓Posted,就像是寄信件一样,当我们把邮件放入邮筒的那一刻起,就标志着信件已发出。至于信件最终是不是会送达到收件人手中,需要邮局系统来保证。在PCIe协议中,这一保证机制是由数据链路层的ACK/NAK协议来完成。对于Non-Posted类型来说,如果请求者在发送完请求事务后一直等待完成者返回完成事务,必然造成链路上的停顿,白白浪费了链路资源。因此PCIe协议规定,请求者发送完一个Non-Posted类型的事务后,可以继续发送其它的事务,而不必一直等待完成者的完成事务。这一过程也称为分离事务(Split Transaction),即请求和完成分离开来。

需要注意的是,请求者不一定就是RC,完成者不一定就是EP或Switch。RC也可以是完成者,EP或Switch也可以是请求者。只有在发送配置事务时,RC与请求者是一致的,EP/Switch与完成者是一致的。

用表格总结一下事务的类型:

Request Type

Non‐Posted or  Posted

Abbreviation

Memory Read

Non-Posted

MRd

Memory Write

Posted

MWr

AtomicOp

Non-Posted

AtomicOp

IO Read

Non-Posted

IORd

IO Write

Non-Posted

IOWr

Configuration  Read

Non-Posted

CfgRd

Configuration Write

Non-Posted

CfgWr

Message

Posted

Msg/MsgD

为了简化学习,可以忽略表中的IO Read/Write。

对于存储读取请求(MRd)和配置读取请求(CfgRd),必然需要有相应的完成响应,否则完成者怎么返回给请求者需要的数据呢。也就是说MRd和CfgRd是Non-Posted类型。参考下图,是一个存储读取请求的例子,首先是EP发起MRd,经过Switch B和A到达RC;RC从相应的内存中将数据取出,并通过CplD将数据发送给EP。

配置写入请求(CfgWr),是RC发给Switch或者EP的,因为Switch或EP的功能取决于该设备是否被配置成功,所以Switch或EP必须返回完成响应给RC,这样RC才能进行后续的操作。CfgWr也是Non-Posted类型。

IORd和IOWr是Non-Posted类型,不再展开了。下图是IOWr的例子。

最后,只有存储写入请求(MWr)和消息请求(Msg/MsgD)是Posted类型,请求者发出请求即认为请求已完成,这样可以大大提高链路效率。至于请求是否真正被发送到了完成者,需要其它的机制保证,后面会介绍。

细心的读者可能发现了一个问题,如果请求者发送了多个MWr或者是多个Msg,那么完成者是以什么顺序接收到的呢?这涉及到PCIe规范中的事务顺序问题。其实这个问题牵扯到所有事务的顺序,后面会详细讲。
2.3 事务层协议-数据包格式
TLP的串行格式如下图所示,包括TLP前缀(Prefixes),TLP包头(Header),有效数据负载(Data Payload)和TLP摘要(Digest)。其中只有TLP Header是必选项,其它三项都是可选项,根据数据包的实际需求而定。PCIe协议是串行总线,单比特传输,也就说发送端的物理层完成TLP并/串转换后,从TLP Prefixes(如果有的话)开始传输,最后传输TLP Digest(如果有的话)。这样做的一个好处是接收端可以提前知道这个数据包的类型,而不用等待全部接收完数据包。

构成TLP的四部分里面,除了TLP Header,其它的都不是必选项,甚至是数据部分。TLP里面最为复杂的也是Header。

TLP的并行格式如下图所示,PCIe协议里面用DW(Double Word,32-bit)来表示并行结构。后面会反复用到DW。

从上图可以看出,长度固定只有Digest部分,为1-DW。其它的三部分,长度都不固定,但都是以DW对齐的。

2.3.1 TLP Header

前面提到过,存储读取/写入事务有两种地址格式:32-bit和64-bit。这个地址包含在TLP Header中,所以对应的有两种TLP Header格式。32-bit地址的Header是3-DW长度,64-bit地址的Header是4-DW(多出来的1-DW正好对应多出来的32-bit地址),如下图。

所有类型的TLP,其Header中的第一个DW格式都是相同的,而第二个DW中的字段随TLP类型不同而变化。

2.3.1.1 通用的Header字段

不管是哪种类型的请求,TLP的第一个DW,也就是前4 Byte格式是通用的,见下图。

其中各字段的含义如下:

  • Fmt[2:0]:TLP的格式,指示TLP的地址格式,或者是否为Prefix

  • Type[4:0]:TLP的类型,Type字段通常与Fmt字段一起编码,来表示TLP的格式

  • TC[2:0]:流量等级(Traffic Class),PCIe协议支持最多8个TC值,通过TC来区分TLP的优先级,从而完成TLP保序和QoS的功能

  • T8 & T9:Tag[8]和Tag[9],最初的Tag只有8-bit,后来增加了2-bit。Tag[7:0]在Header的第二个DW中。此字段由请求者分配,用于区分不同的具体请求,完成者返回完成响应时需带上Tag,这样请求者就知道哪些发出去的请求已经完成,哪些尚未完成

  • Attr[2:0]:3-bit的属性定义,这三个字段有各自的含义,并没有一起做编码,其中Attr[2]表示基于ID排序(ID-Based Ordering),Attr[1]表示松散排序(Relaxed Ordering),Attr[0]表示非监听(No Snoop)。

  • LN:轻量级通知(Lightweight Notification),这是Gen 4中加入的特性,在Gen 6中被取消

  • TH:TLP处理提示(Processing Hints)

  • TD:TLP Digest,指示TLP中是否存在有ECRC部分

  • EP:错误中毒(Error Poisoned),指示TLP Data部分的数据负载是否为“中毒”

  • AT[1:0]:地址类型(Address Type),即地址是否经过转换,仅对带有地址的事务有效,如MRd,MWr,AtomicOp。对其它的事务,该字段保留。在轻量级通知中,该字段有其它含义

  • Length[9:0]:TLP Data部分的大小,数据是DW对齐的,如果实际数据是Byte,则需要借助Header中的其它字段指示。

本节先详细分析一些常用字段,有些不常用字段跟某些特性功能有关,留待后面章节分析。

Fmt[2:0]的编码如下表所示:

Fmt[2:0]

TLP format

000b

3 DW header, no data

001b

4 DW header, no data

010b

3 DW header, with data

011b

4 DW header, with data

100b

TLP Prefixes

Other

保留

Fmt字段通常和Type字段一起,表示TLP类型,这两个字段是Header中最重要的字段。

TLP Type

Fmt[2:0]

Type[4:0]

Description

MRd

000

001

0 0000

存储读取请求

MRdLk

000

001

0 0001

存储读取锁定请求

MWr

010

011

0 0000

存储写入请求

IORd

000

0 0010

I/O读取请求

IOWr

010

0 0010

I/O写入请求

CfgRd0

000

0 0100

Type 0配置读取请求

CfgWr0

010

0 0100

Type 0配置写入请求

CfgRd1

000

0 0101

Type 1配置读取请求

CfgWr1

010

0 0101

Type 1配置写入请求

TCfgRd

000

1 1011

不推荐的TLP类型,以前用于可信配置空间(Trusted Configuration Space),现在不再支持

TCfgWr

000

1 1011

不推荐的TLP类型,以前用于可信配置空间(Trusted Configuration Space),现在不再支持

Msg

001

1 0r2r1r0

不带数据的消息请求,r[2:0]指明消息路由机制

MsgD

011

1 0r2r1r0

带数据的消息请求,r[2:0]指明消息路由机制

Cpl

000

0 1010

不带数据的完成响应事务

CplD

010

0 1010

带数据的完成响应事务

CplLk

000

0 1011

用于存储读锁定,不带数据的完成响应事务

CplDLk

010

0 1011

用于存储读锁定,带数据的完成响应事务

FetchAdd

010

011

0 1100

先获取后相加的原子操作请求

Swap

010

011

0 1101

无条件交换的原子操作请求

CAS

010

011

0 1110

比较并交换的原子操作请求

LPrfx

100

0 L3L2L1L0

本地Prefix,L[3:0]表示具体的Prefix类型

EPrfx

100

1 E3E2E1E0

端到端Prefix,E[3:0]表示具体的Prefix类型

--

--

--

Reserved

链路双方可以借助TC字段实现事务的保序要求。PCIe协议规定,相同TC值的事务需要遵守严格的先后顺序(不开启基于ID排序和松散排序,即不使能Attr[2:1])。同时,PCIe协议还规定,不同TC值的事务之间不需要保序。如果事务的发起方对于事务有顺序要求,则需要将这些事务分配相同的TC值。借助TC,还可以实现QoS功能,不过这需要硬件逻辑配合,放到QoS章节详细讲解。

前面说过,为了提高链路性能,PCIe将Non-posted类型的请求拆分,请求者发出一个请求后即可以继续发送请求,而不必等待完成响应。也就是说请求者可以连续发送多笔请求,比如可以连续发送若干的MRd。至于具体数目,取决于硬件设计的超发(Outstanding)缓冲区深度。此时的一个问题是,这么多超发请求,完成者返回完成响应时,请求者怎么知道对应的是哪笔请求。这就需要请求者给每个请求事务分配一个唯一的标识符,完成响应中带上这个标识符,请求者就知道对应哪笔请求了。这个标识符就是Tag。可能有的人会问,对于Posted类型的请求,比如MWr,不需要完成响应,是不是Tag字段就没有用了呢?的确如此,不过这时PCIe协议对Tag字段另有用处。

AT[1:0]字段表示地址类型,编码如下表。后面讲地址翻译服务ATS时会用到。

AT[1:0]

Mnemonic

Description

00b

Untranslated

TLP Header中的地址是未经翻译的,需要主机侧进行地址翻译

01b

Translation request

地址翻译请求,主机侧的地址翻译代理(Translation Agent)需要返回地址翻译后的页表项给设备(如果支持的话)

10b

Translated

TLP Header中的地址已经过翻译,主机侧可以直接响应该请求

11b

保留


Length[9:0]字段的意思比较简单,就是TLP Data的长度。数据是以DW对齐的,PCIe协议规定,数据最大可以是4KB。在实际应用中,Data的最大长度需要链路双方协商而定,很多时候并不是4KB。

2.3.1.2 存储事务的Header

存储事务支持32-bit地址和64-bit地址两种格式,其完整的TLP Header格式定义如下图。

DW 1,2,3中增加的字段有:

  • Requester ID[15:0]:请求者ID,这是主机分配的PCIe拓扑结构中的唯一值。Requester ID字段与Tag字段合起来组成了事务ID(Transaction ID)

  • Tag[7:0]:事务标签,前面介绍过了

  • Last DW BE[3:0]:数据负载中最后一个DW中的字节使能,用于存储,I/O,配置请求

  • First DW BE[3:0]:数据负载中第一个DW中的字节使能,用于存储,I/O,配置请求

  • Address[64:2]:地址,最低两位强制是00b

  • PH[1:0]:处理提示(Processing Hint),需要配合TH字段使用。如果TH无效,则PH[1:0]保留;TH有效,则PH字段编码表示处理提示,具体含义放到TPH功能部分讲解

这里需要解释一下Last DW BE[3:0]字段和First DW BE[3:0]字段。前面一直在说,TLP Data是DW对齐的,如果实际数据不是DW对齐,则可以借助这两个字段,指出实际数据的起始和结束位置。细心的人可能会问,对于一大段中间有空洞的数据段,该怎么处理?有两个办法,一是将这段数据拆开,用多个TLP传输;二是主机软件或设备软件知道哪些数据无效,在一个TLP将整个数据段传输,然后通过在软件中处理这段数据。

2.3.1.3 I/O事务的Header

I/O事务仅支持32-bit地址(在PCI时代不需要64-bit地址),其完整的TLP Header格式定义如下图。

PCIe协议对I/O事务TLP Header中的字段有一定规则:

  • TC[2:0]字段必须是000b

  • LN字段不适用,保留

  • TH字段不适用,保留

  • Attr[2]保留

  • Attr[1:0]必须是00b

  • AT[1:0]必须是00b

  • Length[9:0]必须是00 0000 0001b

  • Last DW BE[3:0]必须是0000b(I/O事务只有一个DW的数据负载,这个字段没有意义)

2.3.1.4 配置事务的Header

配置事务采用ID路由,因此不需要地址信息,其完整的TLP Header格式定义如下图。

第三个DW中增加的字段有:

  • Bus Number[7:0]:总线编号

  • Device Number[4:0]:设备编号

  • Fcn Number[2:0]:Function编号,与上面两组编号合起来称为BDF,是PCIe拓扑结构中的唯一标识

  • Register Number[5:0]:寄存器编号,与下面的字段一起使用,指示要访问的配置空间中的寄存器地址

  • Extended Register Number[3:0]:扩展寄存器编号

配置事务对TLP Header中各字段的限制:

  • TC[2:0]必须是000b

  • LN字段不适用,保留

  • TH字段不适用,保留

  • Attr[2]保留

  • Attr[1:0]必须是00b

  • AT[1:0]必须是00b

  • Length[9:0]必须是00_0000_0001b

  • Last DW BE[3:0]必须是0000b

2.3.1.5 消息事务的Header

消息事务的路由方式有多种,可以是地址路由,ID路由,隐式路由,其完整的TLP Header格式定义如下图。

新增加的字段是Message Code[7:0],表示消息编码。对于不同的消息类型,DW 2和3(即Byte 8-15)的含义不同。

Type字段的后三位,是消息事务的路由方式,编码见下表。

Type[2:0]

Description

Byte 8-15

000

路由到RC

保留

001

地址路由

地址

101

ID路由

BDF

011

RC广播消息

保留

100

本地路由,终止于接收端

保留

101

收集并路由到RC,仅适用于PME_TO_Ack消息

保留

110/111

保留

保留

消息编码整理如下:

Message Name

Message Code[7:0]

Assert_INTA

0010 0000

Assert_INTB

0010 0001

Assert_INTC

0010 0010

Assert_INTD

0010 0011

Deassert_INTA

0010 0100

Deassert_INTB

0010 0101

Deassert_INTC

0010 0110

Deassert_INTD

0010 0111

PM_Active_State_Nak

0001 0100

PM_PME

0001 1000

PME_Turn_Off

0001 1001

PME_TO_Ack

0001 1011

ERR_COR

0011 0000

ERR_NONFATAL

0011 0001

ERR_FATAL

0011 0011

Unlock

0000 0000

Set_Slot_Power_Limit

0101 0000

Vendor_Defined  Type 0

0111 1110

Vendor_Defined Type 1

0111 1111

Ignored  Message

0100 0001

Ignored Message

0100 0011

Ignored  Message

0100 0000

Ignored Message

0100 0101

Ignored  Message

0100 0111

Ignored Message

0100 0100

Ignored  Message

0100 1000

LTR

0001 0000

OBFF

0001 0010

PTM Request

0101 0010

PTM Response

0101 0011

PTM ResponseD

0101 0011

对于每种类型的消息,TLP header的Byte 8-15各不相同,这里不再赘述,后面用到哪种消息再具体分析。

2.3.1.6 完成事务的Header

对于Non-Posted类型请求,需要完成者返回完成响应事务。完成事务是ID路由方式,其完整的TLP Header格式定义如下图。

新增的字段有:

  • Cpl Status[2:0]:完成状态

  • BCM(Byte Count Modified):仅对PCI完成者有意义

  • Byte Count[11:0]:请求的剩余字节数

  • Lower Address[7:0]:低位地址,对于存储读取请求,该字段是完成事务返回的第一个数据的第一个启用字节的地址

Cpl Status字段的编码见下表:

Cpl Status[2:0]

Completion Status

000

成功完成(Successful Completion,SC)

001

不支持的请求(Unsupported Request,UR)

010

配置请求重试状态(Configuration Request Retry  Status,CRS)

100

完成者中止(Completer Abort,CA)

Others

保留

2.3.2 TLP Prefix

TLP Prefix分为两种,Local TLP Prefix和End-End TLP Prefix。

Prefix的用途是扩展TLP。在PCI时代,TLP的格式就规定好了,随着技术的不断进步,协议中需要添加更多的扩展字段以用于扩展功能。聪明的协议制定者们想出了给TLP加前缀这种方式。在TLP前面添加扩展字段,既不影响旧有的设备和驱动软件,又能增加新特性。

一个Prefix长度是固定1 DW,格式定义如同TLP Header的第一个DW的格式定义。

Fmt字段和Type字段编码指示是哪种Prefix。

Type[4]用来区分是本地Prefix(0b)还是端到端Prefix(1b)。

目前在Gen 5.0中,本地Prefix有以下几种:

端到端的Prefix有以下几种:

除去上面的几种端到端Prefix,还有一种IDE Prefix,用于数据完整性和加密功能。IDE(Integrity and Data Encryption)功能是Gen 5中以ECN(Engineering Change Notice)形式发布的,在Gen 6的基本规范中正式引入。

不同类型的Prefix,Byte 1-3的含义不同,以后用到的时候再具体分析。

2.3.3 TLP Data

Data部分格式很简单,前面已经讲过很多了。Header中的Length[9:0]字段指示数据的大小,编码如下:

大家可以想想,为什么00_0000_0000b表示的是1024 DW,而不是0 DW呢?因为带不带数据是事务类型决定的,所以不需要表示0 DW。

在实际的设备中,很可能不会支持4KB这么大的数据负载。而且,对于存储读取事务的完成事务,PCIe协议规定数据为64-Byte或128-Byte,称为“读取完成边界(Read Completion Boundary,RCB)”。

关于Data的具体规则,这里不详细列出了。

2.3.4 TLP Digest

TLP Digest也叫做ECRC(End-End Cyclic Redundancy Check),如果启用此功能,则PCIe设备对所有发出的TLP增加一个DW,用于端到端的循环冗余校验。

端到端连接是一个网络连接术语,在PCIe中指的是请求者与完成者的连接,不管中间是不是有Switch。点到点连接指的是链路两端的直接连接,比如RC与Switch,Switch与EP的连接。

ECRC顾名思义,完成者负责最后用CRC对TLP其余部分做校验,中间的Switch会将ECRC直接转发(虽然允许Switch检查ECRC,如发现错误后上报错误,但Switch一般不会这么做)。

能看出来,ECRC解决的不是链路传输错误问题,实际上链路传输错误是通过链路CRC(Link CRC,LCRC)来检查的。这会放到数据链路层中介绍。

那么ECRC是用来干什么的呢?在事务层生成TLP一直到送给物理端口发出,中间的过程可能会存在逻辑错误,ECRC就是用来检测这个错误的。

具体的CRC算法就不介绍了。

【待续】

老秦谈芯
交流ASIC技术