编 者 按
读cocotbext-pcie源码,有部分牵涉到数据链路层。虽然自工作以来接触到PCIe还是蛮多的,但一般往往专注在TLP层,对于数据链路层还是接触的比较少的。PCIe Spec洋洋洒洒数千页,也不会从头到尾去通读整个协议。对于cocotbext-pcie里面牵涉到的链路层的ACK/NAK,牵涉到的PCIe背景,聊做记录。
本文仅结合PCIe Spce与cocotbext-pcie做记录。
》ACK/NAK
与TCP协议般,PCIe协议在数据链路层采用滑动窗口ACK/NAK协议来保证数据传输。对于传输层下发的TLP报文,数据链路层会做一次封装:
ACK/NAK报文格式定义如下:
关于滑动窗口机制,往上随便搜下还是蛮多的,不做过多啰嗦。链路层的滑动窗口正是基于TLP Sequence Number。
TLP Sequence Number定义为12 bits。对于发送端而言,其会维护两个变量:
NEXT_TRANSMIT_SEQ:用于存储下一个待发送TLP报文所使用的TLP Sequence Number。初始化时赋值为0.
ACKD_SEQ: 记录从ACK、NAK中返回的 Sequence Number。初始化时赋值为0xfff。对于发送端,规定:(NEXT_TRANSMIT_SEQ - ACKD_SEQ) mod 4096 >= 2048
若上面的条件满足则停止从传输层接收TLP,等待该条件不再成立。
也就意味着在pending中的报文不超过2048(牵涉到报文重传)。
那么由此发送端对于收到的ACK/NAK中协议牵涉到的处理流程Spec定义为:
由于发送端窗口中pending待确认的报文不会超过2048,故若上面1式不成立,那么则意味着用到了尚未使用到的sequence,表示为错误的TLP报文,同样单次增加的ACKD也不会超过2048,否则也应标为错误的TLP。
在式3中,如果条件不成立,则意味着可以窗口前移,可以从replay buffer中移出已发送成功的数据,设置ACKD_SEQ等变量。在cocotbext-pcie中关于dllp的处理也和spec保持一致:
而对于接收端,其定义了:
NEXT_RCV_SEQ:下一个待接收TLP的Sequence Number。
其处理流程为:
这里可以看到,仅当收到的TLP报文的Sequence Number等于NEXT_REC_SEQ时,才会被接收。注意红框中,若条件满足则认为是重复的报文,此时则应发送ACK DLLP报文,否则认为是错误的报文,则应发送NAK。
来看cocotbext-pcie中的处理:
这里在563行取了<而非<=,或可有误。当收到的是期望的报文时(555行),则会更新next_recv_seq、清除nak_scheduled,拉起ack_latency_timer(不必每个TLP均发起一个ACK,但又要确保按照Spec中规定的间隔时间来发送ACK)。而如果收到的是一个重复的TLP(563行),此时将send_ack触发条件拉起,表示要立即发送ACK,而同时关掉ack_latency_timer,避免超时条件触发后多发ACK。否则(567行)将会发送NAK报文。
》One More Thing
关于接收端的ack_latency,协议中有详细的规定,其与flow control定义有相似之处,放在后面结合cocotbext-pcie进行梳理。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !