#通信协议

为什么需要缓存一致

目前主流电脑的 CPU 都是多核心的,多核心的有点就是在不能提升 CPU 主频后,通过增加核心来提升 CPU 吞吐量。每个核心都有自己的 L1 Cache 和 L2 Cache,只是共用 L3 Cache 和主内存。每个核心操作是独立的,每个核心的 Cache 就不是同步更新的,这样就会带来缓存一致性(Cache Coherence)的问题。

举个例子,如图:

有 2 个 CPU,主内存里有个变量x=0。CPU A 中有个需要将变量x1。CPU A 就将变量x加载到自己的缓存中,然后将变量x1。因为此时 CPU A 还未将缓存数据写回主内存,CPU B 再读取变量x时,变量x的值依然是0

这里的问题就是所谓的缓存一致性问题,因为 CPU A 的缓存与 CPU B 的缓存是不一致的。

如何解决缓存一致性问题

通过在总线加 LOCK 锁的方式

在锁住总线上加一个 LOCK 标识,CPU A 进行读写操作时,锁住总线,其他 CPU 此时无法进行内存读写操作,只有等解锁了才能进行操作。

该方式因为锁住了整个总线,所以效率低。

缓存一致性协议 MESI

该方式对单个缓存行的数据进行加锁,不会影响到内存其他数据的读写。

在学习 MESI 协议之前,简单了解一下总线嗅探机制(Bus Snooping)。要对自己的缓存加锁,需要通知其他 CPU,多个 CPU 核心之间的数据传播问题。最常见的一种解决方案就是总线嗅探。

这个策略,本质上就是把所有的读写请求都通过总线广播给所有的 CPU 核心,然后让各个核心去“嗅探”这些请求,再根据本地的情况进行响应。MESI 就是基于总线嗅探机制的缓存一致性协议。

MESI 协议的由来是对 Cache Line 的四个不同的标记,分别是:

状态
状态
描述
监听任务
Modified 已修改 该 Cache Line 有效,数据被修改了,和内存中的数据不一致,数据只存在于本 Cache 中 Cache Line 必须时刻监听所有试图读该 Cache Line 相对于主存的操作,这种操作必须在缓存将该 Cache Line 写回主存并将状态改为 S 状态之前,被延迟执行
Exclusive 独享,互斥 该 Cache Line 有效,数据和内存中的数据一直,数据只存在于本 Cache Cache Line 必须监听其他缓存读主存中该 Cache Line 的操作,一旦有这种操作,该 Cache Line 需要改为 S 状态
Shared 共享的 该 Cache Line 有效,数据和内存中的数据一直,数据存在于很多个 Cache 中 Cache Line 必须监听其他 Cache Line 使该 Cache Line 无效或者独享该 Cache Line 的请求,并将 Cache Line 改为 I 状态
Invalid 无效的 该 Cache Line 无效

整个 MESI 的状态,可以用一个有限状态机来表示它的状态流转。需要注意的是,对于不同状态触发的事件操作,可能来自于当前 CPU 核心,也可能来自总线里其他 CPU 核心广播出来的信号。我把各个状态之间的流转用表格总结了一下:

当前状态
事件
行为
下个状态
M Local Read 从 Cache 中读,状态不变 M
M Local Write 修改 cache 数据,状态不变 M
M Remote Read 这行数据被写到内存中,使其他核能使用到最新数据,状态变为 S S
M Remote Write 这行数据被写入内存中,其他核可以获取到最新数据,由于其他 CPU 修改该条数据,则本地 Cache 变为 I I
当前状态
事件
行为
下个状态
E Local Read 从 Cache 中读,状态不变 E
E Local Write 修改数据,状态改为 M M
E Remote Read 数据和其他 CPU 共享,变为 S S
E Remote Write 数据被修改,本地缓存失效,变为 I I
当前状态
事件
行为
下个状态
S Local Read 从 Cache 中读,状态不变 S
S Local Write 修改数据,状态改为 M,其他 CPU 的 Cache Line 状态改为 I M
S Remote Read 数据和其他 CPU 共享,状态不变 S
S Remote Write 数据被修改,本地缓存失效,变为 I I
当前状态
事件
行为
下个状态
I Local Read 1. 如果其他 CPU 没有这份数据,直接从内存中加载数据,状态变为 E;
2. 如果其他 CPU 有这个数据,且 Cache Line 状态为 M,则先把 Cache Line 中的内容写回到主存。本地 Cache 再从内存中读取数据,这时两个 Cache Line 的状态都变为 S;
3. 如果其他 Cache Line 有这份数据,并且状态为 S 或者 E,则本地 Cache Line 从主存读取数据,并将这些 Cache Line 状态改为 S
E 或者 S
I Local Write 1. 先从内存中读取数据,如果其他 Cache Line 中有这份数据,且状态为 M,则现将数据更新到主存再读取,将 Cache Line 状态改为 M;
2. 如果其他 Cache Line 有这份数据,且状态为 E 或者 S,则其他 Cache Line 状态改为 I
M
I Remote Read 数据和其他 CPU 共享,状态不变 S
I Remote Write 数据被修改,本地缓存失效,变为 I I

AXI

组成部分:

AXI4 协议中包含五种信道,通道之间相互独立且存在差别,通过通道进行通信之前需要使用 VALID/READY 进行握手,Read 和 Write 根据 Master 定义:

  • 读地址信道(Read Address Channel)
  • 写地址信道(Write Address Channel)
  • 读数据信道(Read Data Channel)
  • 写数据信道(Write Data Channel)
  • 写响应信道(Write Response Channel)

还有两种 Component

  • Master component
  • Slave component

通信由 Master 发起,Master 可以对 Slave 进行读数据(read)或写(write)数据。每次读写操作都需要一个地址,读地址信道(Read Address Channel)和写地址信道(Write Address Channel)用于传输地址。在写完数据后,Master 需要确认 Slave 有没有收完数据,Slave 收到完整数据后,会通过写响应信道(Write Response Channel)给 Master 一个反馈(completion),表示写操作已经完成。

VALID/READY 握手机制

AXI 五个信道相互独立,但是使用同一个握手机制来实现信息传递。

在握手机制中,通信双方分别扮演发送方(Source) 和接收方(Destination),两者的操作(技能)并不相同。

发送方置高 VALID 信号表示发送方已经将数据,地址或者控制信息已经就绪,并保持于消息总线上。

接收方置高 READY 信号表示接收方已经做好接收的准备。

当双方的 VALID/READY 信号同时为高,在时钟 ACLK 上升沿,完成一次数据传输。所有数据传输完毕后,双方同时置低自己的信号。

每个通道都有自己的 VALID /READY 握手信号对:

在握手过程中,还会用到 LAST 信号。LAST 信号存在 Write Data Channel 和 Read Data Channel 中,分别表示为 WLAST 和 RLAST,用于标记 burst 的最后一次数据传输,当 slave 接收到 LAST 信号后,说明本次数据传输完成。

双向流控

所谓的双向流控机制,指的是发送方通过置起 VALID 信号控制发送的时机与速度,接收方也可以通过 READY 信号的置起与否控制接收速度。

发送方拥有传输的主动权,但接收方在不具备接收能力时,也能够置低信号停止传输,反压发送方。

握手过程分析

图中 INFORMATION 信号无底色区域表示此时数据已经准备好,已经有新的数据到达。

VALID 信号先到

发送方 VALID 信号早早就到了,但是接收方的 READY 信号在 T2 之前都没有发送。可能接收方在接收其他数据,或者被堵在数据通路上。

过了 T2 后,READY 信号到来,此时开始传输,直到 T3 结束,传输完成。

这里也体现了双向流控机制,发送方的 VALID 信号只要置高,再握手完成之前都不能置低,必须等到接收方 READY 信号置高。

READY 信号先到

READY 信号很自由,可以等待 VALID 信号到来再做响应,但也完全可以在 VALID 信号到来前就置高,表示接收端已经做好准备了。

而且,READY 信号与 VALID 不同,接收方可以置起 READY 之后在 VALID 置高之前都可以随时再置低 READY 信号。

信号同时同时到达

这个最简单,两个信号都等着一个时钟上升沿就完成传输了。

握手信号之间的依赖关系

为了防止死锁发生,信号之间要遵循一些规矩,举例来说,如上面提到的 READY 信号依赖 VALID 信号,但是 VALID 信号不能根据 READY 信号来判断是否数据已准备好,否则将会造成死锁。下面详细解释读写过程中需要遵循的依赖关系。

  • 单箭头指向的两个信号,信号的置高,低没有顺序要求。
  • 双箭头表示箭头所指对象应迟于箭头出发信号发送。

Read transaction dependencies

  • Master 不得等待 Slave 置高 ARREADY
  • Slave 可以在置高 ARREADY 之前等待 ARVALID 置高
  • Slave 能够在 ARVALID 置高之前先置高 ARREADY
  • Slave 必须等待 ARVALID 和 ARREADY 都被置高,然后才置高 RVALID 以表示有效数据可用
  • 在置高 RVALID 之前,Slave 不得等待 Master 置高 RREADY
  • Master 可以在置高 RREADY 之前等待 RVALID 被置高
  • Master 可以在 RVALID 被置高之前置高 RREADY

Write transaction dependencies

  • 在置高 AWVALID 或 WVALID 之前,Master 不得等待 Slave 置高 AWREADY 或 WREADY
  • Slave 可以在置高 AWREADY 之前等待 AWVALID 或 WVALID,或两者都等待
  • Slave 可以在 AWVALID 或 WVALID 或两者都被置高之前置高 AWREADY
  • 在置高 WREADY 之前,Slave 可以等待 AWVALID 或 WVALID,或两者都等待
  • Slave 可以在 AWVALID 或 WVALID 或两者都被置高之前置高 WREADY
  • 在置高 BVALID 之前,Slave 必须等待 WVALID 和 WREADY 都被置高
    • Slave还必须在置高 BVALID 之前等待 WLAST 被置高,因为写入响应 BRESP 必须在写入事务的最后一次数据传输之后才发出信号
  • 在置高 BVALID 之前,Slave 不得等待 Master 置高 BREADY
  • Master 可以在置高 BREADY 之前等待 BVALID
  • Master 可以在 BVALID 被置高之前置高 BREADY

地址结构(Address structure)

AXI 协议是基于 Burst 的,地址结构里声明了一些传输过程中需要的信号,如起始地址,burst 传输长度,传输模式等等。

Burst

在介绍 Burst transfer 之前,需要解释一下什么是 Burst。在手册的术语表中,与 AXI 传输相关的有三个概念,分别是 transfer(beat)、burst、transaction。

  • AXI Transaction:the complete set of required operations on the AXI bus form the AXI Transaction.表示传输一段数据 (AXI burst) 所需的一整套操作;
  • AXI Burst:any required payload data is transferred as an AXI Burst.表示 AXI 待传数据;
  • AXI Beats:a burst can comprise multiple data transfers, or AXI Beats.表示 AXI burst 的组成,一个 Beat 就是一个 transfer。

三者的关系:在 AXI 传输事务(Transaction)中,数据以突发传输(Burst)的形式组织。一次突发传输中可以包含一至多个数据(Transfer)。每个 transfer 因为使用一个周期,又被称为一拍数据(Beat)。

$$
\text{Transaction} = M \text{Burst} ,M \geq 1 \
\text{Burst} = N
\text{Transfer( or Beat)} ,N \geq 1
$$

在地址通道中有三个信号控制进行控制,包括:

ARLEN(Burst Length)

指一次突发传输中包含的数据传输 (transfer) 数量,在协议中使用 AxLen 信号控制。在 AXI4 中,INCR 类型最大支持长度为 256,其他类型最大长度为 16。而 AXI3 中这一数字无论何种模式均为 16。因此 AXI4 中 AxLen 信号位宽为 8bit,AXI3 中的 AxLen 则仅需要 4bit。

ARSIZE(Burst Size)

指传输中的数据位宽,具体地,是每周期传输数据的字节数量,在协议中使用 AXSIZE 信号控制。突发传输数据宽度不能超过数据总线本身的位宽。而当数据总线位宽大于突发传输宽度时,将根据协议的相关规定,将数据在部分数据线上传输。

ARBURST(Burst Type)

Burst Type:AXI 协议中支持不同的 Burst 传输类型,主要分 FIXED、INCR、WRAP。

  • FIXED 传输为地址固定传输,所有传输都会写在同一个地址中。主要应用在 FIFO 的传输中,因为 FIFO 为先入先出,只需要往同一个地址写数据即可。
  • INCR 传输为地址递增传输,可根据具体的配置有固定长度递增和非定长递增。大部分的数据传输都是使用这种方式,尤其是在内存访问中,可以大大提高效率。
  • WRAP 传输为地址回环传输,在一定长度后会回环到起始地址。主要应用在 Cache 操作中,因为 cache 是按照 cache line 进行操作,采用 wrap 传输可以方便的实现从内存中取回整个 cache line。

AXI burst 读操作:master 只需要发送 burst 的起始地址,slave 会根据 burst 的起始地址与 burst 场地自动进行地址计算,将对应的数据与响应发送到 master 侧。

AXI burst 写操作中,也只需要发送 burst 写的起始地址,slave 只需要接受起始地址,然后根据传输的长度将数据传输到对应的地址缓存中。只需要进行一次握手就可以实现地址通道的请求传输,避免系统总线的占用。

数据结构(Data read and write structure)

读写数据结构中声明了几种数据传输方式。

在介绍这些传输方式之前,需要了解WSTRB(Write strobes) 写选通信号。写选通信号 WSTRB 允许在写数据总线上进行 稀疏数据 传输。每个写选通信号对应写数据总线上的一个字节。当写选通断言时,表示写数据总线上对应的字节通道中包含将被更新到 memory 的有效信息。

写数据总线上每 8 位具有一个写选通位,因此 WSTRB[n] 对应 WDATA[(8 x n) + 7 : (8 x n)]。默认情况下 WSTRB = 0xFFFF。也就是所有通路都是通的。

Narrow Transfer

当本次传输中数据位宽小于通道本身的数据位宽时,称为窄位宽数据传输,或者直接翻译成窄传输。如下图,传输总线为 32bit,但是每次只传了 8 bit。

窄传输就是通过 STRB 信号指定有效传输数据的位宽来实现。针对一些特定的寄存器读写,或者在不同数据位宽的总线传输中会使用窄传输操作。如图,第一次传输时,WSTRB 信号为 0x01,WSTRB = b’001,表示 WDATA[7:0] 数据有效。

需要注意在多笔连续的窄传输操作中,STRB 会随着地址递增进行响应的变化,这样方便在系统设计使用中可以方便的将窄传输合并,从而提升系统传输效率。

Unaligned Transfer

AXI 协议规定单次 burst 传输中的数据,其地址不能跨越 4KB 边界。也就是在传输过程中会进行 4K 对齐。但是在某些时候,会期望在非对齐的地址开始一个突发,即非对齐传输。

协议中之所以规定一个 burst 不能跨越 4K 边界是为了避免一次 burst 访问两个 slave(每个 slave 的地址空间是 4K/1K 对齐的)。4K 对齐最大原因是系统中定义一个 page 大小是 4K,而所谓的 4K 边界是指低 12bit 为 0 的地址。

非对齐传输是指有些传输指令不是按照 word 对齐,而是按照 Byte 对齐进行传输。起始地址可能是任意的地址。如下图中,起始地址为 0x1,则在系统上需要按照非对齐的方式进行传输。第一次传输采用 strb 信号指定对应的 Byte 有效,后面的传输可以按照正常的传输进行

下图是一些传输示例,有阴影的格子表示当前字节不会被传输。

图一为正常的对齐传输,传输起始地址为 0x00。

图二为非对齐传输,起始地址为 0x01,第一个格子对应的 WSTRB = b’1110。

图三同上,只是 Burst length 为 5。

图四也为非对齐传输,起始地址为 0x07。对应的 WSTRB = b’1000。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×