PCIe基础分享
简单介绍
之前的专栏主要介绍了DDR - Double Data Rate高速接口,用于处理器和DRAM存储芯片之间的通信。
这里再基于PCIe的SPEC和Mindshare的技术文章,简单介绍下高速并行通信总线规范的PCIe - Peripheral Component Interconnect Express。
PCIe作为用于处理器和各种硬件设备(网卡、显卡等外设)以及硬件设备之间的数据交互和通信的并行信号通信接口,设计阶段肯定需要考虑如下的几个问题:
- Data Bits Skew
多个并行Data信号之间存在skew,如果是通过同一个 CLK 采样就会导致错误的采样结果 - Clock Skew
一个Host端发出的Clock信号需要传输到多个不同的devices,不同devices之间会存在clock skew - Flight Time
信号从Host端发送到Device端的传递时间,如果Flight Time过大(大于一个采样时钟周期),就会出现数据miss,导致后续signal全部混乱 - Timing Margin and Clock Rate
不同信号之间存在skew,采样速率越快,多个信号之间的timing margin越难控制
为了解决上述的问题,我们在之前的DDR专栏提到过DDR作为并行通信接口针对此类问题的部分设计(例如通过Training来修正skew)。
而PCIe则是采用了SerDes技术,也就是SERializer(串行器)/De-Serializer(解串器)的简称,将低速的并行信号转化为高速低压差分信号并通过串行里链路发送,再接收串行输入串行信号并还原为低速并行信号。
所以在PCIe通信接口里,时钟信号和Data数据都会被混合到一起成为串行信号,等发送到对端之后再恢复出时钟信号和Data数据。
而时钟恢复的过程也有专门的硬件设计支持,简单的组成结构如下:
PCIe的端对端传输中,转化后的串行信号都会通过Tx/Rx通道上的差分信号进行传输,一组Tx/Rx通道就构成了一个lane。
而一组PCIe接口设备之间最多支持32Lane的传输宽度,这么多Lane组成的2个PCIe接口设备之间的信号通路就是一个link。
不同lane的数量也就定义了PCIe的Link带宽。
与DDR不同的还包括PCIe PHY的架构支持的信号调制也从NRZ向PAM4过度,一次Clock携带的数据量从0/1(2种情况)变为了00/01/10/11(4种情况)
拓扑结构
下图是PCIe 3.0 Spec中部分的拓扑结构图,简单来看,除了具备Function的PCI Express Endpoint (PCIe Device)之外,主要有以下几个组件:
- Root Complex
管理所有的PCIe Devices枚举信息和各个Device的Function信息,为后续devices routing 提供信息 - Switch
类似于一个Hub,管理部分PCIe Devices枚举信息和Device的Function信息,向Root Complex汇报,具体的消息发送通过一定的routing结构 - PCI Express to PCI/PCI-X Bridge
管理PCI/PCI-X等早期的协议和相关设备 - Bus
一个switch可以挂载多个Bus,Bus用于挂载Device
并且PCIe枚举时采用的是深度优先,每个PCIe Endpoint枚举成功后才会继续向下枚举next Endpoint。
向下枚举时不同bus下的Device/Function num会重新编号,类似于图中的Bus3和Bus4下面都有Dev0/Function0,但是物理设备和功能上都是不同的Devices和Function。
热插拔场景下,如果像上图中bus3和bus4之间本来还有一个Bus资源,设备需要接入到这个bus下,在基于深度优先的枚举原则下,那么就需要重新枚举(新加入的device占用为Bus4,Bus4及后续Bus都需要Number+1)
因此在真实PCIe枚举过程中,会预留一些空的device number,导致当前相邻的Bus Number数值差异会有点大,便于快速枚举热插拔设备(像上图中的bus3 & bus4可能就变成了bus3 & bus10)
这部分逻辑设计由SW控制,保证如果单个device枚举失败,就会把device视为空,不会影响其他devices。
PCIe设备层级设计
从上图可以看到,在Pcie Device之间的通信中,Device Core需要通过3个不同的Layer进行数据的封装/解包完成通信。
详细内容如下图所示:
从上图左侧可以看到,Transfer/Receive的device中不同的Trans/Data/Physical layer对Data进行了不同的封装/解包。
上图右侧是更为详细的封装/解包过程(一个TLP - Transaction Layer Packet payload length为4096 byte),下面基于上图右侧的三个层进行简要介绍:
Trans Layer
传输层主要的操作对象就是TLP - Transaction Layer Packet,TLP Packet中header会包含地址和command类型(R/W)信息,Data Payload包含Data信息,ECRC用于校验。
而TLP本身包括的信息可以分为以下几种类型。
Info Type | Comment |
---|---|
Memory | 面向Memory,往Address指向的空间进行R/W,支持burst |
IO | Input/Outpt的硬件端口,访问空间有限,不支持burst |
Configuration | 属于Device Function里的Object信息,只供RC - Root Complex处理 |
Message | 类似Interrupt这类misc信息 |
在不同的TLP传输过程中,也需要通过不同的Request请求发起,Request类型包括了Non-Post和Posted两种。
Posted代表不需要对端回复信息。
Non-Posted代表需要对端回复,譬如Memory Read,一定需要对端回复一个CplD(Completion with Data),IO Read/Write一定需要回复一个Cpl(Completion without Data) 。
而在TLP传输中,传输层也定义了不同TLP的QoS(Quality of Service),包括了:
Component | Comment |
---|---|
Traffic Class(TC) | 优先级信息,存放在Header中 |
Virtual Channel(VC) | 基于TC,对Buffer中的多个Packets根据Prior/Ordering等信息进行发送仲裁 |
Arbitration Logic | 基于VC进行仲裁 |
Ordering | 排序规则仅适用于 VC |
可以理解为QoS决定了不同的Data传输对象可以根据TC里的Priority决定是否要在一个严格order的VC传输。
譬如设计的时候,如果是IO传输对象,必须严格要求order,但是memory传输对象就不用那么严格要求ordering。
并且传输层还支持了Flow Control。
Flow Control就是保证Ordering前提下,根据带宽管理Virtual Channel做流量限制,并且在Link Packet之外,会单独地让DLLP - Data Link Layer Packet建立一个CRC校验包。
如果VC Buffer快满了,DLLP控制反压流量,告诉发送方当前的流量需要控制在多少。
Data Link Layer
保持了Flow Control的机制,对于Packet增加了CRC校验以及Ack响应机制。DLLP - Data Link Layer Packet也是在这一层实现。
在Data Link Layer,发送端根据接收方返回的ACK保证每笔数据准确地发送成功,如果长时间没有收到ACK或者收到了Nak,则会把数据存储到Retry Buffer进入重发队列,如果多次fail,就会上报error。
接收端根据DLLP模块校验CRC确认每笔数据都是准确的,再根据Packet中的Sequence Number进行周期性地ACK/Nak回复。
这里的ACK/Nak + CRC + LCRC + ECRC就简单地保证了一笔传输的正确性。
这里Switch之间的传输ACK对应关系就是1a/1b, 2a/2b, 3a/3b, 4a/4b这样,不会有requester到Completer的直接ack,都由switch中继。
Physical Layer
物理层的任务就比较繁杂了:
- Byte striping and de-striping
Byte分配到不同物理lane的拆包/重组任务 - Scrambling and de-scrambling
信息传输的加扰/解扰任务 - Encode/Decode
数据的编码/解码 - Elastic Buffer
由于串口上的Clock传输存在多拍/少拍/skew等问题,弹性buffer这里用于恢复串口上的Clock信号为SoC片内Clock - Link Training
对Link训练保证delay满足信号传输需求 - Order Set
就是信息传输的顺序配置,结构组成就是右下角的图 - Link Width/Data Rate
发送方和接收方确定双方都能接受的Link带宽和传输速率 - Lane Reversal
Lane上的两端Rx/Tx可以根据具体布线方案任意端接,如果是Rx-Rx/Tx-Tx,就需要配置Lane - Reversal Polarity Inversion
由于都是差分信号传输,同样可以根据具体布线方案任意端接,如果是T-T/C-C,就需要配置这个 - Bit/Symbol Lock Per Lane
每个lane上的bit/symbol(就是8b到10b转换后的head/tail部分)信息都是lock不变的,不会出现重排序 - Lane to Lane skew
Byte分配到不同物理lane拆包后,需要保证lane之间的skew不影响Byte重组 - Serialized & De-Serialized
并串/串并信号转换
FSM状态机
下图是PCIe的状态机信息,以这个图为基础介绍下不同的状态代表的含义
- Detect
通过电容充放电的速度判断是否端接了Rx - Polling
包括 Bit Lock + Symbol/Block Lock + Lane Polarity + Lane Data Rate 的配置和检查。
Lock可以理解为两个PCIe设备之间相互发送特殊码进行协商,确定通信配置是否一致。 - Configuration
根据刚才Lock的商量结果,配置Link Width + Lane Number + Lane Reversal + De-Skew - Recovery
主要用于重新建立链接或者重新配置Link/Lane的场景,常见的场景有:
设备挂了,Lock掉了,L0直接进Recovery
设备Rate变了,重新分配Deskew Lane2Lane,L0直接进Recovery
L2/L1SS等 Power State变化,低功耗场景下,设备主要的Clock变化,需要重新进Detect/Recovery
L0s这类浅度睡眠场景,短时间不会进Recovery,但是长时间还是会切state
PS. Low-Power场景下最耗费时间的通常是上下电,而不是Clock切换或者FSM切换。 - L0
非低功耗模式,最基础的State,发Traffic信息流都在这个阶段。 - L0s
没有Traffic阶段的低功耗模式。
由SW手动控制或者由ASPM (Active State Power Management State) 控制PCIe是否在没有Traffic阶段自动进入低功耗模式.
或者在有Traffic阶段自动退出低功耗模式。 - L1
低功耗State,仅支持两种方式PCI-PM和ASPM控制Power。
这个模式下,如果两端设备都没有拉低CLKREQ(默认为弱上拉),那就会直接进入L1SS State。 - L2
低功耗State,几乎所有Logic Off,只保留部分Vaux供电最低Logic。
如果外部Device送进来Wake模拟信号(Beacon),以及Controller过来的片内Wake信号(SideBand)可以被唤醒。
PCIe枚举
首先来看枚举相关的寄存器配置,如下图所示:
在每个Function中会有256字节的寄存器配置空间,寄存器里存储的前64字节是Header信息,主要有用于Endpoint的Type0和用于Switch/bridge的Type1两种类型。
除了最基础的地址信息,设备ID,Status,指令等信息之外,两个Header之间的区别主要如下:
Type0类型Header不需要关注Routing相关信息(Bus Number/Latency Timer/Base/Limit)。
Type1类型Header不需要关注subsystem ID信息。
在枚举过程中,BAR - Base Address Register通过地址路由则是负责路由的基准之一,另外一种是BDF(Bus Device Func)通过ID路由。
BDF枚举后,RC(Root Complex)就可以访问任意device,RC就需要初始化每一个Device里的BAR。
一个BAR是32bits长度。
BAR初始化过程如下:
- 往BAR里写0xFFFFFFFF
- 读回来发现一部分写不了
- 推算出BAR大小,SW根据读到的信息(IO/MEM, 32/64 bits decode, prefetch/non-prefetch)开始写Base Address
- 对应设备的BAR信息固定
- 后续Endpoint接收data就会根据BAR信息匹配到对应的address,写入data
BAR初始化之后,地址路由主要通过RC配置的header实现地址路由。
Switch的Type1 Header里的IO/(P/NP Memory) Base/Limit信息决定可寻址的区间。
Type0 header里的BAR决定Endpoint的寻址空间(譬如BAR: 0xF9000, range就是0xF9000~0xF9FFF)
这种路由方式决定了BAR不能重复。
在BAR枚举之后,一个TLP - Transaction Layer Packet 数据包经过Switch或者 PCIe 桥片时采用哪条路径,最终到达EP或RC,也需要通过路由的方式确定。
而不同的TLP则是有不同的路由适用场景: