4.3 PCIE链路层发送端Ack/Nak机制的组成要素


这是剖析PCIE协议的第23篇文章

内容简介

2024-01-29

全文共1279字,阅读大约需要4分钟,本文内容分为两个部分,

1、概述性的介绍下Ack/Nak机制,会忽略很多细节

2、详细的介绍发送端为了保证可靠传输做了哪些什么


01

简介Ack/Nak的整体机制


    一个TLP从发送方给到接收端,那么发送方是如何知道已经送达并正确接收了呢?


    这就是通过数据链路层独有的DLLP包来实现的。这里还想再强调一下,DLLP包始于数据链路层也之余数据链路层。


    如果接收端成功收到就返回一个称为Ack的DLLP,如果接收端发现收的有问题,就回反馈一个称为Nak的DLLP告知一下。大致是这么回事,但仅通过这么一个机制远远达不到可靠传输,所以链路层还要其他机制配合,共同保证可靠传输。


如下图的两条线路:


1、发送端收到事务层的TLP后添加sequence和LCRC发送出去,接收端收到后去除TLP的sequence和LCRC发送到事务层。


2、接收端根据判断结果反馈Ack或Nak数据包到发送发,发送端收到后根据DLLP内容做出响应处理。




02

发送端保证可靠传输的方法


    本文主要介绍发送端数据链路层为了保证TLP的可靠传输,提供了哪些功能模块,做了哪些工作。大致分为三个部分,橙色框图为第一部分,绿色为第二部分,黄色为第三部分。



第一部分

    对收到的事务层TLP添加序列号和LCRC并写入缓冲区,缓冲区写到一定程度会反压事务层的写操作


第二部分

    添加序列号。NEXT_TRANSMIT_SEQ(缩写NTS)表示下一个要添加的序列号,在向“Assign Sequence Number”传递一个值后会加一,AS是ACKD_SEQ的缩写,表示接收端发过来的Ack或Nak的序列号,如果差值大于2048也会反压事务层


第三部分

    处理接收端反馈的DLLP,这里是Ack或Nak,处理顺序如下:


1、进行CRC校验,校验失败直接丢掉


2、序列号比较,序列号比较又分为多种情况,大致可以这样理解,如果是Ack,则接收端接收正常,发送端根据Ack中的序列号,将缓存区里面不大于该值的TLP释放掉,如果是Nak说明接收端收到的数据包存在有问题的情况,同样根据Ack包中的序列号将缓存区中不大于该值的释放掉,其它的进行重传。


03

保证可靠传输的组件


    根据上图我们可以看出,保证发送端可靠传输是多个模块协调配合共同完成的,本节就所包含的组件挨个介绍一下。


3.1

NEXT_TRANSMIT_SEQ

    下一个传输序列号,或者说就是一个循环数。这是一个12bit数,表示下一个传入的TLP包序号。随着TLP包的到来不断递增,计数到最大值4095后回到0重新开始。所以说,在一定时间内,每个TLP都有唯一的一个序列号。这个序列号在链路层对保证不丢包有着非常大的作用。


3.2

LCRC Generator

    如下,一个添加在TLP包尾部的32位的CRC,用于包的错误检测。




3.3

Replay(Retry) Buffer

    这是一个数据缓存Buffer,按照链路层接收TLP包的顺序存储于此,待收到了接收端发来的ACK会将对应的部分TLP包释放掉,如果收到的NAK就要判断清除哪些TLP包,重传哪些数据包,这个缓冲区的意义所在其实也体现在了支持重传。每个缓冲区都存储了一个完整的TLP,包括序列号(2字节)、头部(最多16字节)、可选有效数据载荷(4KB)、可选ECRC(4KB)和LCRC(4字节)。


    缓冲区的大小没有强制规定,缓冲区大小在设计时应不会轻易写满了阻塞事务层的TLP包,也不至于太大造成资源的浪费。对于大小的设定需考虑来自接收端Ack的延迟、物理链路的延迟以及包长度等。


3.4

REPLAY_TIMER Count

看这个英文名称,重放(回放)定时器。发送端发送TLP包后就会启动计时(计数),如果到了预期时间接收端没有收到Ack/Nak,发送端认为没有发送成功,会启动重传机制重新再发一遍。


    只要发送端发送了TLP,这个计时器就会启动。如果计时器启动了又发了新的TLP包,这个计时器也不会被重置,只有收Ack/Nak或溢出了才会重置。如果接收端的buffer是空的,自然也不会启动计时。


3.5

REPLAY_NUM Count

这个是重传计数器,当收到Nak或者REPLAY_TIMER超时了,会重新将缓冲区的数据包发出去。但也得有个限制,要是有不可恢复的错误,就卡死在这了,这个计数器就是防止卡死的。如果收到了ACK,且经过分析这个ACK认为之前发送的TLP包对方都接收到了,那这个计数器也会被清零。

    这是个2bit的计数器,当计数从11变成了00,数据链路层会自动强制物理层重新训练链路。


3.6

ACKD_SEQ Register

对于发送端来说,这是来自接收端的Ack/Nak数据包里面的序列号,是一个12bit数。在链路不link时候会重置为0xFFF,收到Ack/Nak后更新锁存,待下次收到后再更新。


    锁存序列号与接收的进行比较,如果晚于新接收的就表明接收方收到了以发送的TLP包。这里需要注意用到了晚于一次,是因为这是一个不一定连续的循环数,可能锁存的是4095,但收到的是2,这时候不能用大于小于来比较。如果收到的是Ack的序列号,且是最新的,则会把buffer里面的对应TLP包释放掉,REPLAY_NUM和REPLAY_TIMER也会重置。


3.7

DLLP CRC Check

DLLP包里面也带有CRC校验的,不过不是32bit是16bit,这个模块是检查这个校验是否正常。如果校验失败,就会把数据包直接丢掉。


欢迎关注公众号点击【资料下载】领取相关资料








快来扫描下方二维码关注公众号,领取站内所有相关资料,所有哦~

有建议、有需求、有疑问、联系我

<