前面的文章介绍了TLP的几种类型以及TLP的包结构。这篇文章来详细地聊一聊Non-Posted Transaction(包括Ordinary Read、Locked Read和IO/Configuration Writes)与Posted Writes(包括Memory Writes和Message Writes)。
Non-Posted Transaction
o Ordinary Reads
下图显示的是一个Endpoint向System Memory发送读请求(Read Request)的例子。
在这个例子中,Endpoint的读请求通过了两个Switch,然后到达其目标,即Root。Root对读请求的包进行解码后,并从中识别出操作的地址,然后锁存数据,并将数据发送至Endpoint,即包含数据的Completion包,ClpD。需要注意的是,PCIe允许每个包的最大数据量(Max Data Payload)为4KB,但实际上经常需要发送大于4KB的数据。因此,常常一个读请求会对应多个ClpD,即将大于4KB的数据分成多个包发送。如果遇到错误,则Root会通过Completion包告知相应的Endpoint。
注:Root向发送请求的Endpoint发送Completion包,是通过Request包中的BDF信息(Bus,Device和Function)进行查找对应的Endpoint的。关于BDF,会在后面的文章详细地介绍。
o Locked Reads
Locked请求实际上是PCIe为了兼容早期的PCI总线而设置的一种方式,对于非PCI兼容的设计中,是不允许使用Locked操作的。并且也只有Root可以发起Locked请求操作,Endpoint是不可以发起Locked请求操作的。下图显示的是一个简单的Locked Read请求操作:
Locked Read主要用于支持一种叫做Atomic Read-Modify-Write操作,这是一种高优先级且不可被打断的操作。主要用于测试链路状况等任务(针对PCI设备,PCIe设备禁止使用Locked操作)。此外,Locked操作采用的是目标存储寻址(Target Memory Address)来寻找Legacy Endpoint(PCI设备),而不是采用前面介绍的BDF。
o IO/Configuration Writes
下图是一个Non-Posted IO写操作的例子。和Locked操作一样,IO操作也是为了兼容早期的PCI设备,在PCIe设备中也是不建议使用。
Posted Writes
o Memory Writes
前面的文章有所提及,PCIe中的Memory写操作都是Posted的,因此Requester并不需要来自Completer的Completion。一个简单的Memory Writes例子如下图所示:
因此没有返回Completion,所以当发生错误时,Requester也不会知道。但是,此时Completer会将错误记录到日志(Log),然后向Root发送包含错误信息的Message。
o Message Writes
和其他的几种类型不太一样,Message支持多种Routing方式。比如Requester可以将Message发送至一个指定的Completer,但是不管指定的Completer是不是Root,Root都会自动的收到来自任何一个Endpoint发送的Message。此外,当Requester是Root的时候,Requester还可以向所有的Endpoint进行广播发送Message。
不得不说,Message机制的提出帮助PCIe总线省去了很多PCI总线中的边带信号。PCI中很多用于中断、功耗管理、错误报告的边带信号,在PCIe中都通过了Message来进行实现了。