大家好,欢迎来到IT知识分享网。
✨前言:
在PCIe通信协议中,ACK(Acknowledgement)和NAK(Negative Acknowledgement)是两种重要的信号,它们用于控制和确保数据传输的可靠性。
✨ 一、Ack/Nak说明
Ack/Nak是一种由硬件实现的,完全自动的机制,目的是保证TLP有效可靠地传输。Ack DLLP用于确认TLP被成功接收,Nak DLLP则用于表明TLP传输中遇到了错误。
如上图所示,发送方会对每一个TLP在Replay Buffer中做备份,直到其接收到来自接收方的Ack DLLP,确认该TLP已经成功的被接受,才会删除这个备份。如果接收方发现TLP存在错误,则会向发送发发送Nak DLLP,然后发送方会从Replay Buffer中取出数据,重新发送该TLP。
Ack/Nak机制内部的详细结构图如下图所示:
下面对图中的各个Elements分别做一个简单地介绍。
首先是发送端的Elements:
来个大图特写:
· NEXT_TRANSMIT_SEQ Counter NEXT_TRANSMIT_SEQ
Counter,即NTS计数器,是一个12位的计数器。当数据链路层处于DL-Down状态或者复位时,该计数器会被初始化为0。该计数器只会执行加一操作,也就是说当其到达最大值4095时,在进行加一操作则会变成0(Roll
Over)。该计数器用于产生下一个待发送的TLP的序列号(Sequence
Number)。每一个序列号都是与一个TLP所唯一对应的,可以说这个序列号正是整个Ack/Nak机制的关键。 · LCRC
Generator LCRC产生器用于产生一个32位的CRC值,其作用于整个TLP和其对应的序列号。 · Replay
Buffer Replay Buffer是Mindshare书中的叫法,在PCIe Spec中,这个Buffer的名称叫做Retry
Buffer。Replay Buffer中按照传输顺序,存储了整个TLP、序列号和LCRC,如下图所示:
当发送端收到来自接收端的Ack DLLP时,会将Buffer中相应的TLP(包括对应的序列号和LCRC)移除;如果接收到的是Nak DLLP,则会将Buffer中响应的TLP(包括对应的序列号和LCRC)重新发送给接收端。
· REPLAY_TIMER Count
REPLAY_TIMER是一种看门狗定时器,当该定时器溢出,则表明发送端已经发送了一个或者多个TLP,但是并未收到来自接收端的应答信号(Ack/Nak)。此时,发送端会将Replay
Buffer中的TLP重新发送,并将看门狗定时器重启。
只要发送端发送了任何TLP,该定时器便会启动,在接收到来自接收端的应答信号之前都会持续地运行。当收到应答信号之后,定时器会立即被清零。此时如果Replay
Buffer仍然有TLP(表明还有TLP被发送,但是仍未得到应答),定时器又会被立即被重新启动。如果Buffer中是空的,则定时器不会被重新启动,直到新的TLP被发送。
· REPLAY_NUM Count
这是一个2位的计数器,用于记录同一个TLP发送失败的次数,当其值从11b变为00b时(溢出了,表示尝试发送某个TLP失败了4次),数据链路层会自动地强制物理层重新进行链路训练(即LTSSM进入Recovery状态)。当完成链路训练之后,便会重新发送之前发送失败的TLP。
当发送端接收到来自接收端的Nak DLLP或者发送端的看门狗定时器(REPLAY_TIMER)溢出时,该计数器都会被加一;当接收到Ack
DLLP时,该计数器则会被清零。 · ACKD_SEQ Register
ACKD_SEQ寄存器用于存储最近接收到的Ack或者Nak
DLLP中的序列号。当复位或者数据链路层处于无效状态时,该寄存器会被初始化为全1。关于ACKD_SEQ寄存器的具体用法会在后续的文章中,用例子的形式详细说明。
· DLLP CRC Check 接收端在接收到来自发送端的DLLP后,首先会检查其DLLP
CRC,如果发现有错误,则会直接将其丢弃,认为其实无效的DLLP。 然后是接收端的Elements:
首先来一张大图特写:
· LCRC Error Check 顾名思义,LCRC Error
Check用于检查接收到的TLP是否存在错误。如果存在错误,则将对应的TLP直接丢弃,然后产生一个Nak
DLLP发送给发送端,让其重新发送该TLP。 · NEXT_RCV_SEQ Counter
NEXT_RCV_SEQ是一个12位的计数器,即Next Receive Sequence
Number,其值为已经成功接收的TLP的序列号加1。主要用于检查当前接收到的TLP是不是应该接收到的TLP。
如果NEXT_RCV_SEQ和当前接收到的TLP中的序列号的值相等,则认为这是一个有效的TLP,但是接收端并不会立即向发送端发送Ack
DLLP,而是等到AckNak_LATENCY_TIMER溢出时才向发送端发送Ack DLLP。也就是说,一个Ack
DLLP可能会对应多个TLP,接收端不会每成功接收到一个TLP便向发送端发送Ack DLLP。
如果当前接收到的TLP中的序列号小于NEXT_RCV_SEQ(且差值不超过2048),则认为该TLP之前已经成功发送过了,此次是重复发送。需要注意的是,PCIe Spec认为重复发送并不是一个错误,只是直接将该TLP丢弃,并没有Nak或者Error Reporting,但是会返回一个包含有上一次成功接收的TLP的序列号(NRS-1)的Ack DLLP给发送端。
如果当前接收到的TLP的序列号大于NEXT_RCV_SEQ,表明传输过程中漏掉了一些TLP。此时,接收端会返回Nak DLLP,并直接丢弃该TLP。
一个简单的例子如下图所示:
· NAK_SCHEDULED Flag NAK_SCHEDULED是一个标志位,当接收端产生Nak
DLLP时,该标志位会被置位。当接收端成功接收到有效的TLP时,该标志位会被清零。需要特别注意的是,当该标志位处于置位状态时,接收端不应产生其他的Nak
DLLP。 · AckNak_LATENCY_TIMER
AckNak_LATENCY_TIMER定时器会在接收端成功接收到有效的TLP,且并未向发送端返回Ack
DLLP之前运行。当AckNak_LATENCY_TIMER定时器溢出时,接收端会立即向发送端返回Ack
DLLP(携带的序列号为NRS-1,即一个Ack对应多个有效的TLP)。无论接收端返回Ack还是Nak,该定时器都会被复位,但是只有当接收端再次收到有效的TLP时,该定时器才会被重新启动。
该定时器(REPLAY_TIMER)的值是由PCIe Spec规定的和Lane的数量与Max_Payload有关,Gen1的值如下图所示:
Gen2(5GT/s)如下图所示:
注:该定时器(REPLAY_TIMER)的值是AckNak_LATENCY_TIMER定时器值的三倍。而REPLAY_TIMER的值则如下表所示(Gen1和Gen2):
最后,介绍一下PCIe Spec中推荐的包优先级顺序。我们知道,PCIe总线通信中,存在多种类型的包,包括TLP、DLLP和Ordered Sets等。为了能够是总线达到最优的传输效率,PCIe Spec推荐对这些包的优先级做如下的设置:(💡 当然这只是推荐,并没有强制厂商一定要这要去实现)
- Completion of any TLP or DLLP currently in progress (highest priority)
- Ordered Set
- Nak
- Ack
- Flow Control
- Replay Buffer re‐transmissions
- TLPs that are waiting in the Transaction Layer
- All other DLLP transmissions (lowest priority)
✨二、NAK和ACK机制举例
🌟例1 Example of Ack
Step1 设备A准备依次向设备B发送5个TLP,其对应的序列号分别为3,4,5,6,7; Step2
设备B成功的接收到了TLP3,并将NEXT_RCV_SEQ从3加到4,但是设备B没有立即向设备A返回Ack(此时AckNak_LATENCY_TIMER尚未溢出);
Step3 设备B又成功地接收到了TLP4和TLP5; Step4
假设此时AckNak_LATENCY_TIMER溢出了,则设备B会向设备A返回一个带有序列号为5的Ack
DLLP。同时,设备B将AckNak_LATENCY_TIMER复位,但是并未重新启动,直到设备B成功地接收到了TLP6。 Step5
设备A接收到了Ack5,将REPLAY_TIMER和REPLAY_NUM复位,然后将Buffer中的序列号5(和5之前)的TLP备份移除;
Step6 一旦设备B接收到TLP6,AckNak_LATENCY_TIMER又会被重新启动。
🌟例2 Ack with Sequence Number Rollover
Step1
设备A准备依次向设备B发送序列号为4094,4095,0,1,2的TLP,注意第一个发送的是TLP4094,最后一个发送的是TLP2。也就是说序列号Rollover了;
Step2
设备B成功接收到TLP4094~TLP1后,假设此时AckNak_LATENCY_TIMER溢出了,则设备B向设备A返回Ack1 DLLP;
Step3
设备A接收到Ack1,并将Buffer中的序列号为1(和之前的,包括TLP4094~TLP1)的TLP备份移除。同时将REPLAY_TIMER和REPLAY_NUM复位。
🌟例三 Example of Nak
Step1 假设设备A准备依次向设备B发送序列号为4094,4095,0,1,2的TLP; Step2
设备B成功地接受了TLP4094,并将NEXT_RCV_SEQ加1,变为4095; Step3
设备B接收到了TLP4095,但是该TLP并未通过CRC校检(即存在错误)。此时无论AckNak_LATENCY_TIMER处于何种状态,设备B都会立即向设备A返回Nak4094(注意返回的Nak
DLLP中的序列号为上一次成功接收的TLP的序列号)。同时设备B将AckNak_LATENCY_TIMER停止并复位; Step4
设备B会一直等待设备A向其发送TLP4095,但是设备A却并不知发生了什么,在接收到设备B向其返回的Ack/Nak之前,会继续发送TLP0~TLP2,只是设备B会直接忽略这些TLP。
Step5 当设备A接收到来自设备B的Nak4094
DLLP时,会将Buffer中的TLP4094(和之前的TLP)移除,并从TLP4095从新开始发送。同时,将REPLAY_TIMER和REPLAY_NUM复位。
Step6 由于设备A接收到的是Nak,而不是Ack,因此设备A会重新启动REPLAY_TIMER并将REPLAY_NUM加一;
Step7
一旦设备B成功地接收到TLP4095,设备B便会清除NAK_SCHEDULED标志位,并将NEXT_RCV_SEQ计数器加一,同时重启AckNak_LATENCY_TIMER。
🌟例4 Example of Lost TLPs
Step1 假设设备A准备依次向设备B发送TLP 4094,4095,0,1,2; Step2
设备B成功地接收了TLP4094~TLP0,并向设备A返回Ack0,此时设备B的NEXT_RCV_SEQ为1; Step3
设备A接收到设备B返回的Ack0,从Buffer中移除相应的TLP备份; Step4
设备B接收到了TLP2(而不是TLP1),也就是说TLP1在传输过程中丢失了。此时,设备B会直接将TLP2丢弃,并将NAK_SCHEDULED标志位置位,同时向设备A返回Nak0
DLLP; Step5 设备A接收到Nak0
DLLP后,会将Buffer中的TLP0(以及之前的,如果有的话)移除。同时,从TLP1开始,重新向设备B发送。
🌟例5 Example of Bad Nak
Step1 设备A准备依次向设备B发送TLP 4094,4095,0,1,2; Step2
设备B成功的接收了TLP4094~TLP0,但是由于AckNak_LATENCY_TIMER尚未溢出,所以设备B没有立即向设备A返回Ack
DLLP; Step3 设备B发现TLP1中存在错误,于是向设备A返回Nak0 DLLP,并将NAK_SCHEDULED标志位置位;
Step4 设备A发现其接收到的Nak0 DLLP中也存在错误(CRC校检不通过),于是直接丢弃了Nak0; Step5
然而设备B却一直在等待设备A向其发送TLP1,在其成功接收TLP1之前,设备B不会返回任何Ack或者Nak,不管设备A向其发送什么(除TLP1之外的)。设备B的NAK_SCHEDULED标志位也一直保持置位;
Step6
尴尬的是,设备A并不知道设备B想要其重发TLP1(由于没有成功接收到Nak0)。因此,设备A会继续向设备B发送之后的TLP,但是由于一直没有接收到设备B的Ack/Nak
DLLP,设备A的REPLAY_TIMER会在一段时间后溢出; Step7
当设备A的REPLAY_TIMER溢出后,设备A会向Buffer中的所有TLP都重新发送一遍,并重启REPLAY_TIMER,同时将REPLAY_NUM计数器加一;
Step8
设备B会再次接收到TLP4094~TLP0,但是这在之前就已经成功接受到过了。因此设备B会直接将其丢弃,且不会像设备A返回任何的Ack或者Nak
Step9
此时,设备B再次接收到了TLP1,并未发现错误(成功接收)。于是,设备B将NAK_SCHEDULED标志位清零,并重启AckNak_LATENCY_TIMER,将NEXT_RCV_SEQ加一。
📚 总之,ACK和NAK是PCIe通信中保证数据传输可靠性的重要机制,通过这两种信号的使用,可以有效避免数据错误和丢失的问题,确保系统的稳定运行。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/149730.html