PCIe协议学习之-Ack/Nak协议
作者:互联网
Ack/Nak协议是PCIe Data Link layer中最重要的内容。它用来保证TLP的可靠传输。它是基于Retry buffer实现的,Tx发出TLP后,将原始的TLP存到Retry buffer里面,当RX通过Ack DLLP告知已经正确收到TLP后才会将TLP中相应的TLP移除。当Rx通过Nak DLLP告知Tx有TLP出现错误后,Tx可以将Retry buffer中的相应TLP resent出去。
Tx和Rx的逻辑框图如下:
TX端
1)SEQ Num分配
DL层从TL层收到TLP后,要为其分配SEQ Num, 这个SEQ Num来自一个12bits的NEXT_TRANSMIT_SEQ(NTS), NTS从0开始每次加1,到4095后又rollover到0.
2)Ack/Nak DLLP
在Ack DLLP中,AckNak_Seq_Num的含义是Rx成功接收的最后一个TLP的SEQ_NUM。在Nak DLLP的含义也是Rx成功接收的最后一个TLP的SEQ_NUM,而不是失败的TLP的SEQ_NUM. 也就是说,当Tx收到Ack/Nak后,等于或者小于SEQ_NUM的TLP都可以从Retry buffer中移除了。
2)AckD_SEQ(AS)
含义是最近一次被成功接收了的SEQ_NUM, AckD应该是Acknowledged的意思??
3)Retry Timer
当Tx方向发出了TLP而没有收到Ack/Nak,则这个timer就处于running. 当收到Ack后,reset timer, 将retry buffer里面等于或者小于SEQ_NUM的TLP移除后,若retry buffer里面还有TLP,则重新启动timer, 若retry buffer里面没有TLP则保持timer为0,直到后面Tx又发出了新的TLP, timer重新启动。每当timer超时后,Tx就会将Retry buffer里面的TLP全部重新发一遍然后重启这个timer.
4) Retry Num Count
用于记录retry的次数,2bits, 初始值为0,每进行一次retry, Retry Num都会加1,当Tx收到Ack DLLP,且SEQ_NUM>AS(即有一个或多个TLP被正确接收了)清0。当Retry Num从3 rollover到0时,DL通知PL进行retrain, PL的LTSSM跳到Recovery状态。Retrain完成后,Tx将resent retry buffer里面的TLP.
5) 上诉逻辑图中,只进行了SEQ_NUM==AS的判断,为什么没有进行大于和小于的判断?
只要SEQ_NUM != AS, 就将AS更新为SEQ_NUM的值,理论上讲,NTS-AS的差值不会超过最大值的一半即2048,如果超过了2048就在逻辑上代表NTS<AS, 会上报DLL Protocal error。回到上面的问题,之所以不进行SEQ_NUM<AS的判断,是因为不会出现这种情况,否则就要上报错误。值得注意的是,NTS达到最大值后都是rollover的,因此判断SEQ_NUM的大小都是通过两者之差是否小于最大值的一半来判断的,这一点于Flow control里面的counter类似。可以结合下图进行理解,比一个数大的就是其右边的2037个,再往右的2048个都是比自己小的。
6) DLLP CRC Check
当TX端收到来自RX端的Ack/Nak DLLP后,会首先进行DLLP的CRC校验,如果校验失败则直接丢弃DLLP,同时上报Correctble Error (if enable)。不用担心DLLP丢弃后会丢失信息,RX端后续发过来的Ack/Nak会报告RX端最后收到的Good TLP的SEQ_NUM。
RX端
1)NRS
NEXT_RCV_SEQ(NRS) counter用来记录RX端期望下一次接收到的TLP的SEQ_NUM, 12bits, 初始值为0。当收到1个good TLP后该counter会加1。
2)LCRC Check
收到TLP后进行LCRC Check,如果Pass则继续SEQ_NUM Check。如果LCRC fail则认为是一个bad TLP,丢弃bad TLP并将NAK_SCHEDULED置1,如果NAK_SCHEDULED的前值为0则立即发送Nak DLLP(SEQ_NUM=NRS-1)。NAK_SCHEDULED的存在,是为了避免重复发送Nak DLLP, Nak DLLP只会在NAK_SCHEDULED的上升沿发送。当收到good TLP后将NAK_SCHEDULED清0。
3)SEQ_NUM Check
3.1 当TLP的SEQ_NUM==NRS, 这正是期望的,意味着收到一个good TLP。TLP收下来并转交给TL层,NRS加1。需要发送Ack DLLP,但不需要马上就发,等到AckNak_Latency_Timer超时后再发送SEQ_NUM=NRS-1的Ack
3.2 当TLP的SEQ_NUM<NRS,证明收到的TLP之前已经收到过了。导致这种情况可能是由于之前RX端发过去的Ack由于某些原因TX端并没有正确收到,然后TX端的Retry Timer超时后将Retry Buffer里面的TLPs又发了一遍。这种情况TLP直接丢弃,然后发送一个SEQ_NUM=NRS-1的Ack DLLP(不用等AckNak_Latency_TImer超时)。
3.3 当TLP的SEQ_NUM>NRS, 证明一定有TLP丢失了,因为good TLP一定是按顺序接收的,不应该出现SEQ_NUM>NRS的情况。这种情况下,bad TLP被丢弃,如果NAK_SCHEDULED为0则将其置1并发送一个SEQ_NUM=NRS-1的Nak DLLP。
4)AckNak_Latency_Timer
当RX方还有good TLP没有被Ack时,该timer running。当收到Good TLP后开始启动,当发送了Ack/Nak后清0。当该timer超时后,发送一个SEQ_NUM=MRS-1的Ack DLLP。
标签:TLP,SEQ,Ack,Nak,DLLP,PCIe,NUM 来源: https://blog.csdn.net/HelloQili/article/details/115737108