[FPGA 实现及PCIe IP 核知识点] 写为什么不能超越写?和你想象的一样吗?

(本文介绍了PCIe Ordering Rule中的A2a,即一个Posted Request必须不能够超越另一个Posted Request的情况)

在了解PCIe Ordering Rule之前,我们先看看什么是Posted Request。

在PCIe的事务性操作(Transaction)中,主要包括以下几种:

Memory Transaction,I/O Transaction,Configuration Transaction和Message Transaction。

每一种事务操作都需要一个发送端发送请求,另外一个接收端接收该请求。如果某个事务要求接收端在接收到了某个事务请求后,需要返回完成报文(Completion),那么该请求被称为Non-Posted Request。如果接收端接收到某种事务请求(Request),不需要返回完成报文的,该请求被称为Posted Request。

以上常见事务请求中,Memory Read(MRd),I/O Read(IORd),I/O Write(IOWr),Configuration Read(CfgRd)和Configuration Write(CfgWr)属于Non-Posted Request,而Memory Write(MWr)和Message(Msg)request属于Posted Request。

下面,我们看看Ordering Rules的表格(部分):

1.jpg

其中高亮的位置就是今天要谈的一种场景A2a :一个Posted Request不允许超越另一个Posted Request。这其中就包括了Memory write和Message requests。

那为什么一个Posted Request不能超越另一个Posted Request呢?

有人说这和写覆盖相关,例如我们向某个地址先写5,然后写10,如果没有出现超越的现象,那这个地址存放的数据最终是10。但如果写10的操作超越了写5的操作,最后这个地址存放的就不是10,而是5了,所以得出写不能超越另外一个写的结论。

乍一看还挺有道理的,但这种情况仅仅在写同一个地址的时候有效。如果PCIe是想表达上述的意思,似乎表达上缺少了对同一地址的限定,但以目前的描述看,地址不同的写似乎也需要严格保序。

那么,这究竟是怎么一回事呢?

我们可以先把Posted Request按照Request Type进行区分。属于Posted Request的包括Massage Request和Memory Write Request。

对于Message Request来说,大部分的都需要保持顺序,因为从作用来看,Message Request经常被用来通知本地发生的事件,这些事件的发生自然会有序的关系。如果Message Request承担了控制对方行为的任务,那么自然也有序的要求。当然,如果不同子类型的Message Request之间,大部分情况是不用保序的。为了简单起见,PCIe标准默认同方向,来自同一个ID*的Message Request需要强保序。当然,针对某些子类型的Message Request也保留了控制bit。

对于Memory Write Request来说,需要从Producer-Consumer Ordering Model(制造者-消费者保序模型)说起。如下图所示:

2.jpg

整个过程分为四步:

Producer准备好一堆数据,例如4笔Cacheline的数据,通过MWr request写入到存储单元,例如DRAM中。

然后Producer需要通知Consumer数据已经存放好,并告知数据的地址。

Consumer发起MRd request。

从已知的地址处读回4笔Cacheline数据。

由于MWr request不需要completion,四笔数据发出后,Producer就可以做其他的事情,例如发送Notify到Consumer。如果Consumer距离Producer和Storage都很近,那么会存在一种情况就是当Consumer从告知的地址处读回来的数据并不是Producer的数据。Producer的写数据还在路上。如下图所示:

3.jpg

由于Consumer读早了,#2和#3Cacheline还是旧数据,新数据还在路上。

为了解决这个问题,我们可以增加一个标志位,例如#4,Producer在传整笔数据前,先把#4位置清空成0,表示#0-#3数据还没有准备好。后续如果Consumer需要读取#0-#3数据前,必须先读#4的标志位,如果标志位是0,代表还不能读,如果标志位是1,代表#0-#3的数据已经准备好了,可以读取。如下图所示:

4.jpg

于是步骤换成7步:

Producer写标志位等于0

Producer写#0-#3数据

Producer写标志位等于1

Producer通知Consumer并附上数据地址

Consumer查询约定标志位是否为1

当标志位为1,Consumer发起#0-#3的读操作

Storage返回#0-#3的数据

不难看出,整个流程中存在一个假设前提就是Producer对标志位写1的操作,必须在写#0-#3的后面,这个就是在这个模型中我们要保证的序。

如果写操作允许乱序,#4位置的写很有可能会超前到前面的#0-#3的写,那么Consumer可能会得到错误的信息,以为#0-#3已经准备完成而获得旧数据。

同样,为了简单起见,PCIe标准默认对写操作规定了强保序。但从模型中我们不难发现,#0-#3的写是可以乱序的,#4写1的操作必须和前面的写保序就可以了。

下面,我们用真实的DMA传输过程来描述一下整个过程,如图所示:

5.jpg

PCIe设备,例如网卡从网络上收取到数据报文

网卡过滤收取到的报文,将不属于本节点报文丢弃。

配置DMA引擎,指向所分配的Receive Queue,如图中#1所示。

将数据报文存放在Receive data FIFO中。

DMA引擎将数据传送到主存中指定的Receive Queue位置,如图中#2所示。

设备更新Receive descriptor的状态(Flag),如图中#3所示。

设备发送Interrupt通知core进行处理,如图中#4所示。

不难看出,步骤5,6之间存在风险,如果状态比实际的数据先到达,可能出现core获取的数据是旧数据的情况。

好了,希望这个简单的DMA例子可以帮助你理解A2a的规则。我们有机会再介绍其他的Ordering Rules。

ID* - Bus: Dev: Fuc

Reference
PCI Local Bus Specification Revision 3.0
PCI Express® Base Specification Revision 5.0 Version 1.0

编辑 重设标签(回车键确认) 标为违禁 关闭 合并 删除
匿名用户

匿名