Advertisement

NOC总线架构拓扑介绍

阅读量:

总线架构总结
总线架构的重要性
总线架构是系统中数据传输的核心通道,决定了数据如何在不同组件之间流动。其设计直接影响系统的性能、扩展性和可靠性。选择合适的总线架构可以显著提升系统效率,而错误的选择则可能导致性能瓶颈或功能失效。

总线架构的类型
(1)PCI总线
结构:由总线、控制器、设备和处理器组成。
工作原理:处理器发起访问请求,控制器处理请求并传递给设备,设备再将数据返回给处理器。
特点:支持单对多的通道,适合单处理器系统。
应用场景:传统PC系统中的I/O总线。
(2)AXI/ACE总线
结构:支持多对多的通道,能够处理复杂的请求和响应。
工作原理:处理器和设备通过AXI/ACE总线交互,实现数据和命令的传输。
特点:

  • 支持多OT(操作)和多P(路径)。
  • 通过缓存一致性协议确保不同处理器组看到相同的内存内容。
  • 不支持自举请求,必须由总线发起。
    应用场景:多处理器系统(如ARM Cortex-A7处理器)。
    (3)NoC(网状总线)
    结构:采用网状拓扑结构,支持大量设备连接。
    工作原理:设备通过缓存和总线接口交换数据,总线负责数据的传输。
    特点:
  • 支持高扩展性和低延迟。
  • 通过硬件一致性的实现(如PCIe/ACE)解决缓存一致性问题。
    应用场景:AI/ML模型训练、自动驾驶等高需求场景。

总线架构的挑战
(1)缓存一致性问题
问题:不同处理器组需要访问同一块内存,必须保证一致性。
解决方案:

  • 使用PoU(Point of Unification)和PoC(Point of Coherency)定义缓存操作的范围。
  • 通过硬件一致性的实现(如PCIe/ACE)解决缓存一致性问题。
    (2)延迟与吞吐量
    PCI总线:单对多的通道结构导致延迟较高,但适合简单

NOC总线架构拓扑介绍

总线通道是处理器与内存之间的基础连接,配置一个仲裁器即可实现处理器发送来的读写请求的转发功能。在处理过程中,我们设定处理器和缓存组合作为主设备,将内存控制器作为从设备。处理器发送读写请求时,如果是读操作,总线将处理器的读写请求(包含地址信息)转发给内存控制器,等待其返回数据信息。在数据传输阶段,若读操作无误(基于ECC或奇偶校验机制),则完成读操作流程。对于写操作,处理器将写请求(包含地址和数据)通过总线转发给内存控制器,完成写入操作后,控制器将确认信息返回总线,最终确认信号返回至处理器,完成整个写操作流程。

上述流程有几个关键点。首先,处理器的读指令被划分为地址请求和数据完成两个阶段。同样,写指令分为地址和数据请求,以及写入确认完成两个阶段。值得注意的是,内存控制器无法主动发起读写操作。例如,当发生读写错误时,必须通过中断机制,由处理器发起对内存控制器状态的读写请求。此外,未完成的读写指令会被标记为OT,总线可以处理多个OT请求。然而,总线处理多个OT并不意味着处理器能够同时发送多个请求,尤其是读操作。因此,可能的瓶颈依然在处理器。

我曾经历过几次这样的情况,当运行某个驱动程序时,系统会出现短暂的停滞。尽管其他设备在出现中断时仍能响应,但若发生异常后系统又能重新启动。如果我们更换上文中的内存控制器为设备控制器,那么这种现象就容易理解了。假设处理器向设备发送读请求,而设备未作出回应,那么处理器就会陷入等待状态。我观察到的处理器类型包括PowerPC和ARM,这些系统均未为这类情况设计超时机制。在没有中断的情况下,处理器无法切换到其他线程(如Linux等操作系统的独占模式),就会陷入长时间等待状态,系统似乎陷入停滞。然而,某些设备控制器能够自动检测这类超时情况,并通过中断机制触发相应的异常处理或中断处理。在中断处理程序中,通常会报告错误信息,调整返回地址,跳过当前指令,继续执行后续指令,从而恢复系统运行。另外,某些处理器在发生特定异常时会自动跳转到下一条指令,从而避免长时间停滞。

回归至总线系统。在AXI/ACE协议架构中,读写通道分隔开来,因其之间并无必然联系。具体而言,总线定义了五个关键字段:读操作地址字段(主端发送至从端),读操作数据字段(从端传输至主端),写操作地址字段(主端发送至从端),写操作数据字段(主端传输至从端),以及写操作确认字段(从端反馈至主端)。值得注意的是,读写两大类操作之间并无固定先后顺序。然而,在同一类操作内部,存在明确的操作顺序,例如地址字段应最先发送,随后是数据字段,可允许多次发送形成突发操作序列。写操作确认字段则是从端在数据被写入颗粒存储体后发送的反馈信息。对于内存控制器而言,确认反馈必须在数据最终写入颗粒存储体后发送,而非在数据被暂存于缓存中时发送。

对于同一个通道,如果收到连续的指令,它们之间的顺序是怎样的呢?AXI/ACE协议规定,这种顺序是可以被打乱的。以读操作为例,前后两条读指令的数据返回是可以被打乱顺序的。这里存在一个问题,总线如何区分前后两条读指令?很简单,在地址和数据组中增加几根信号,作为标志符,来区分0到N号读请求和完成。每一对请求和完成使用相同的标志符。有了这个标志符,就不需要等待前一个请求完成才能开始下一个请求,而是可以交替进行,这样就可以实现总线的顺序总线(OT),从而极大提高效率。当然,还需要提供相应的缓冲来存储这些请求的状态。而最大OT的数量取决于缓冲容量和标志符中小的那个。原因很简单,如果缓冲或标志符用完了,但所有的读操作都是请求,没有一个能完成怎么办?那只好让新的请求等待。于是就有了AXI/ACE总线的一条规则,同一个读或写通道中,相同标志符的请求必须按顺序完成。

有时,处理器也会使用这个标志符作为其内部的读写请求标志符,例如,Cortex-A7处理器就是这么操作的。这样就不太合适,因为这就等于给自己设置了限制,即最大输出的OT数量不得超过总线每通道的标志符数量。当一个处理器组包含四个核时,这种情况很可能用不上,因为人为限制了OT数量。

最后,读写通道之间是没有规定次序的,哪怕标志相同。

在这一环节可能会遇到一个关键问题,需要特别关注。读写指令遵循一个默认原则,即在相同地址或存在重叠时,访存必须保持顺序。此外,当访问的内存类型为设备时,必须确保访存顺序与指令保持一致。如何在总线上实现这一要求呢?总线通过检查地址来确保顺序,具体表现为内存访问前后乱序地址不得超出64字节范围,而设备访问前后乱序地址则需限制在4KB以内。

在AXI/ACE架构中,读写通道的比例为1:1。然而,在 typical 程序中,读取操作的频率通常高于写入操作。值得注意的是,写缓存操作伴随着缓存行填充 linefill(读),而读缓存操作会导致缓存行移除 eviction(写)。此外,合并操作和顺序调整的引入,进一步证明了并非总是遵循读写指令的比例。Freescale PowerPC的总线CCB架构显示,其读写通道的比率为2:1。我不知道为什么ARM为何未采用类似设计以提高效率,或许一比一的比例是基于手机典型应用统计得出的最优选择。

我们现在已经能够在脑海中构建一对读写通道中读写操作的传输模型。在考虑多个主从设备的组合时,呈现怎样的状态?叠加是否过于简单?这实际上触及了总线设计中最本质的挑战,即拓扑结构的优化。

在ARM现有的所有总线产品中,基于拓扑结构的划分可以将其分为三类: NIC/CCI系列采用交叉矩阵架构(Crossbar),CCN系列基于环状总线(Ring),而NoC系列则采用网状总线(Mesh)结构。每类总线产品都有其独特特点和适用场景。交叉矩阵架构虽然限制了主从设备数量,但其通信效率最高,能够实现1至2个时钟周期内完成读写操作。下图展示了典型的5x4交叉矩阵结构:

在这里插入图片描述

基于我观察到的数据,在28纳米工艺节点上,采用5×4的布局时,该总线的频率可达300MHz。当主从对数量进一步增加时,由于扇出和走线的增加,电容及走线长度的提升,此时,为了提升频率,需要在总线上增加更多的寄存器。这会相应增加从主设备到从设备的延迟。即便维持5×3的布局,若希望将频率提升至500MHz,可考虑采用更先进的工艺,如16纳米工艺,该工艺下可实现800MHz;或可增加2至3级寄存器,这将导致读写操作的延时可能达到4至5个总线时钟周期。若总线与处理器的倍频比率为1:2,仅在总线上耗时就可能需要至少20个处理器时钟周期。倍率提升至4倍时,时间会相应拉长至40个时钟周期。值得注意的是,处理器访问二级缓存的延迟通常在10个左右处理器周期内即可完成。为了进一步优化平均延迟,可以增加总线上的OT数量,但受制于处理器的OT数量限制,对于顺序处理器,OT数量通常仅限于1至2个。因此,在仅增加频率以支持更多主从设备的情况下,采用环状总线(如CCN系列)将更为合适,如下图所示:

在这里插入图片描述

CCN总线上每一个端点,除了能与相邻的两个端点实现通信外,还可以增加两个组件,例如处理器组、三级缓存和内存控制器等。在端点内部,总线是交叉的;而在端点之间,总线是环状的。这种设计使得总线频率在一定程度上突破了连接设备数量的限制(当然,这仍然受到布线等其他因素的制约),在16纳米工艺下,该系统可运行至1.2GHz以上。然而,这种结构也会带来节点间通信平均延迟增大的问题。为降低节点间的通信延迟,建议将频繁交互的节点布置在靠近的位置。

在某些系统中,为了连接更多的设备同时要求更高的频率,传统的环状总线已经无法满足需求。因此,NoC应运而生,如图所示。

在这里插入图片描述

在该图中,刚才提到的交叉矩阵表,可以作为整个网络的某一部分。而连接整个系统的,是位于网络节点(NoC)内的节点。每个节点都是一个小型路由,它们之间传输的,是异步数据包。这样,就不必维持路由和路由之间很大数量的连线,从而提高频率,也能支持更多的设备。当然,坏处就是更长的延迟。根据观察到的数据,在16纳米工艺上,频率可以达到1.5GHz。并且它所连接的每个子模块之间,频率和拓扑结构可以是不同的。将需要紧密协同工作的设备,如CPU集群和GPU,放置在同一子网中,以降低通信延迟。

或许你未曾察觉,这些网络总线在这些网络设备上都能找到对应的原型。从设备间的直连,到环状局域网的构建,再到交换机与路由器的配置。这些网络设备的拓扑结构在本质上是相通的。

在实际的ARM生态系统中,交叉矩阵、环状网络和网状拓扑的使用情况是怎样的呢?通常在手机芯片中采用交叉矩阵结构,而网络处理器和服务器则倾向于使用环状网络。此外,网状拓扑在手机芯片中也有广泛应用。需要特别说明的是,网状拓扑的应用并非因为设备数量过多,而是由于AXI/ACE协议不支持一次传输拆分成多个,导致内存控制器无法有效处理。交叉访问是实现内存管理的一种关键机制,其定义可以从总线的角度进行区分。第一种定义是主设备和从设备之间的多个读写请求完成次序可以打乱,我们已经对此进行了详细说明。第二种定义则是将一个主设备的一次读写请求分别发送到多个从设备进行处理。需要解释这种传输方式的原因在于,手机内部对GPU、显示控制器和视频控制器的内存带宽要求极高。以1080p屏幕为例,其每秒刷新率高达60帧,涉及200万个像素,每个像素占用32位颜色信息,再加上8层图层深度,总计数据传输速率达到4GB/s。然而,1.6GHz的DDR3控制器仅能提供10GB/s的理论带宽,实际带宽因各种因素限制,通常能维持在70%的利用率即为不错。对于处理器和其他控制器而言,如何解决这一带宽瓶颈是一个关键问题。解决方案是将一个地址拆解为多个请求,分别发送至不同的内存控制器。需要强调的是,这一任务应由总线系统负责,而非处理器自行处理。这是因为处理器不具备拆分读写请求的能力,总线系统则需要清楚当前有多少个内存控制器在工作。因此,处理器应专注于发送请求,而总线系统则负责将请求拆解,并在完成所有数据收集后统一返回处理结果。

遗憾的是,AXI/ACE总线本就无法实现一对多的交叉访问。原因很简单,这会导致死锁。假设我们有两个主设备和两个从设备,通过交叉矩阵连接。M1先发送读请求,标志符为1,依次发送到S1和S2,等待完成。随后M2也进行同样的操作,标志符为2,依次发送到S2和S1。在此情况下,假设S2发现通过调整返回数据的顺序可以提高效率,它会采取这一措施。然而,M1却无法接收S2返回的数据,因为根据同标志符必须顺序处理的原则,M1必须先等待S1的数据返回。同样,S1也无法将数据发送给M2,因为M2此时也在等待S2的反馈。这种相互等待的情况就会导致死锁的产生。解决方法是,任何一个主设备,不能同时向多个从设备发送请求。因此,多内存控制器的交叉访问功能无法实现。

基于分组传输的网状总线结构不存在这种问题,所有分组都是异步发送的,彼此之间没有依赖关系。因此,专门致力于NoC总线开发的公司Arteris应运而生,它们抓住这一机会,将支持多内存控制器同时访问的总线系统成功销售给 majority的手机和平板芯片设计公司。相比之下,ARM虽然也提出了解决方案Arachne,但这些解决方案尚未在实际应用中得到广泛应用。

现在,多为8核的中低端手机在市场中较为普遍。根据ARM架构的设计原则,在一个处理器组内部,最多可配置四个处理器核。因此,需要将两个处理器组部署在系统中,以满足计算需求。在处理器组之间的通信,包括大小核的协调,必须借助一致性总线。每个处理器组内部也需要一致性机制,其原理与外部一致,这里就不赘述了。使用软件实现一致性相对便捷,但需要手动将缓存内容复制到下一级缓存或主内存。对于一个具有64字节行大小的64KB缓存,需要执行1000次缓存刷新操作,每次操作耗时100纳秒,若总时延OT设置为4,则总时延将达到25微秒。这种计算时延对于处理器来说是不可接受的。为了解决这一问题,ARM引入了专门的协处理器来处理一致性需求。为了硬件化这一过程,ARM开发并引入了支持硬件一致性总线的专用方案,下图展示了第一代设计:CCI400。

在这里插入图片描述

CCI400是如何实现硬件一致性的呢?简单来说,就是处理器组C1向总线发送一个包含地址信息的特殊读写指令,总线将该指令转发给另一个处理器组C2。处理器组C2接收到请求后,根据地址逐步查找二级和一级缓存,如果自身具备缓存资源,则会返回数据或执行相应的缓存一致性操作,这一过程称为snooping(监听)机制。为了确保设备不主动发起请求,这种机制需要两组主从设备,每组处理器各占一个主设备和一个从设备。这样可以实现两组处理器之间的数据一致性。而像DMA控制器这样的设备,由于本身不具备缓存资源,也不需要被监听,因此仅配置为从设备,如图中所示的橘黄色部分。根据ARM的定义,具有双向功能的接口称为ACE,而仅具备监听功能的接口则称为ACE-Lite。这些接口除了具备AXI的读写通道外,还额外配置了监听通道,如图所示。

在这里插入图片描述

新增的监听通道同样包含地址字段(从端到主端)、响应字段(主端到从端)以及数据字段(主端到从端)。在每组信号中,这些字段都与AXI机制相似,均用于支持多OT操作。具体而言,如果主设备检测到数据(称为命中),则数据通道会被使用;如果没有命中,那么从设备会告知主设备未命中,无需进行数据传输。由此可知,对于上文中的DMA控制器而言,它永远无法向其他设备传输数据,因此ACE和ACE-Lite不需要数据组,这正是它们的主要区别。

在读通道中增加了一个额外的寄存器RACK,其作用是:当设备将读操作的数据发送至主设备时,主设备无法得知数据何时到达,因为插入寄存器导致总线延迟增加。如果此时设备需要向主发送新的监听请求(针对同一地址A),就会出现一个问题:主设备是否已经接收到了之前发送的地址A数据?如果尚未接收,则主设备会告知监听未命中。然而,实际上地址A的数据已经发送至主设备,并且主设备应返回命中。引入RACK后,设备在收到主设备确认之前不会发送新的监听请求,从而避免了上述问题。同样地,写通道上的WACK也遵循这一机制。

我们曾计算过NIC400的延迟情况,引入CCI400的硬件同步后,是否意味着访问速度有所提升呢?需要明确的是,硬件一致性设计的初衷并非为了提升速度,而是追求软件上的简化。然而,实际情况未必如预期般理想。因为当给定一个地址时,我们无法预知该地址是否位于另一组处理器的缓存中,因此无论如何都需要执行额外的监听操作。当发生未命中时,这个监听操作便显得多余,因为我们仍需从内存中获取数据。这种多余的操作必然会导致额外的延迟,计算结果表明,10个周期用于判断,加上10个周期用于数据获取,总计20个周期,较之前增加了100%。然而,当发生命中时,虽然总线周期数依然需要10个周期,但通过从缓存中获取数据而非从内存中获取,可以节省一定时间。综合来看,当命中率超过某一阈值时,整体性能仍能获得提升。

从实际应用的角度来看,除了经过特殊设计的程序外,命中率通常在10%以下。因此,为了提升性能,我们不得不考虑一些优化措施。一种可行的方法是,无论结果如何,总线都需要先从内存中捕获数据。一旦数据捕获完成,我们才能根据监听结果决定返回哪部分数据。这种方法的缺点是不可避免的功耗开销,因为总线必须完成两次数据传输。此外,在内存访问频率极高的情况下,这种做法反而会降低整体性能。

另一种方法就是,如果已知数据不在其他处理器组缓存中,那么可以让发出读写请求的主设备特别地不需要监听,这样总线就不会处理这个请求。这个方法的缺点在于需要软件干预,尽管其代价并不算高,只需在分配操作系统页面时设置相应的寄存器即可,但对程序员的要求较高,必须充分理解目标系统的具体情况。

研发团队在设计CCI总线时,采用了创新的方法以显著提升性能。他们通过引入一个专门的过滤器模块来处理总线事务,这个模块被称为Snoop Filter。实际上,这个过滤器模块相当于一个缓存机制,它整合了处理器组内部的一级和二级缓存状态信息,用于监控缓存命中情况。值得注意的是,数据缓存模块被省略了,因为其主要功能只是判断缓存命中与否。这种设计的好处在于,所有监听请求可以在总线内部完成处理,从而节省了大约10个总线周期的时间,同时功耗表现优于直接访问内存。然而,这种优化方式也带来了额外的开销,即增加了约10%的一级和二级缓存容量。在操作过程中,当Snoop Filter中的某一行缓存被替换时,需要执行无效化操作(Invalidate),以确保数据一致性。这一操作不仅需要在过滤器内部完成,还需要同步更新所有相关的一级和二级缓存,这增加了额外的处理负担。具体来说,过滤器需要实时跟踪缓存更新状态,以确保一致性。在实际测试中发现,这种无效化操作的频率相对较低,一般不超过5%的可能性。然而,在某些特定测试场景中,操作频率较高,这会导致过滤器的性能表现受到影响。

以上的想法在CCI500中实现,示意图如下:

在这里插入图片描述

在经过实际性能测试后,CCI设计团队发现,总线瓶颈转移到了访问该监听过滤器的窗口。这一现象实际上隐藏了上文提到的反向无效化问题,且总是先于该问题被发现。通过增加该窗口的大小,在测试中发现,当每个主从接口都同时发送大量数据(假设主从设备的带宽为无限大,并且存在一主多从的配置,且数据传输存在交叉现象)时,在主从设备接口处经常会出现等待状态。具体而言,数据准备好后,设备却无法及时接收。为此,增加了缓冲区以存储这些数据。然而,这种缓冲区的增加带来了稍大的面积和功耗开销。需要注意的是,这个缓冲区与用于存储OT状态的缓冲区并无重复功能。

基于实测数据,经过所有改进后,新的总线带宽性能实现了50%以上的提升。同时,频率值也成功提升了至800Mhz。然而,这一数据结果仅作为一个粗略统计,考虑到处理器和内存控制器的OT数量有限,被监控数据的占比可能会有所变化,命中率也会随之调整,此外,监听过滤器的规模不同也会导致结果出现差异。

在手机芯片领域,总线需支持多优先级传输,即QoS。显示控制器等设备对实时性要求较高,同时处理器组的请求同样重要。实现QoS并非难题,只需将各类请求存入缓冲区,按优先级依次传送。测试发现,当各设备请求数量过多且频繁时,缓冲区很快填满,导致无法及时处理高优先级请求。为了解决这一问题,我们将缓冲区按优先级分组,每组仅接收当前或更高优先级的请求,从而避免了阻塞。这一方法与网络防拥塞设计原理不谋而合。

为了支持多时钟和电源域,从而使得每一组处理器能够动态调节电压和时钟频率,ADB(Asynchronous Domain Bridge)作为CCI系列总线的一种搭配,能够有效提升系统性能。然而,它在实际应用中存在一定的性能代价,在倍频为2时,信号经过ADB需要额外增加一个总线时钟周期;当倍频提升至3时,所需增加的时钟周期数量会相应增加。对于对访问延迟有严格要求的系统而言,这一额外的时间成本不容忽视。如果系统设计中不需要额外的电源域支持,我们完全可以选择不使用ADB,从而可以显著减少延迟。

和一致性相关的是访存次序和锁,容易混淆访问次序和锁的概念。假设我们有两个核C0和C1。无论何时,同一地址A0的访问数据必须保持一致,这体现了一致性。在C0内部,保证先访问A0再访问A1的顺序仅需依靠壁垒指令。然而,当C0和C1同时运行时,若它们分别访问同一地址A0,且需要按照顺序访问,此时就需要使用锁。因此,单一核的单线程环境仅需壁垒指令来保证访问顺序,而多核多线程环境则需要锁来确保顺序一致性。一致性确保了在进行锁操作时,同一变量在缓存或内存的不同副本中保持一致。

ARM的壁垒指令分为强壁垒DSB和弱壁垒DMB。我们都知道,读写指令会被分割为请求和完成两个阶段。强壁垒指令要求在执行完上一条读写指令后,才能开始处理下一条指令的请求。而弱壁垒指令则只要求在发出上一条读写指令请求后,就可以继续处理下一条指令的请求,并且可以保证,指令执行完成后,前一条指令已经完成。显然,后一种情况在性能上更为优异,其吞吐量OT值大于1。然而,测试结果表明,在多处理器组的环境中,如果将壁垒指令传输至总线,将显著降低整体系统的性能。因此,在新的ARM总线架构中,不支持壁垒指令的传输,必须在芯片设计阶段通过配置选项告知处理器组处理壁垒指令,不要将其发送至总线。然而,这并不影响程序中的壁垒指令,因为处理器组会在总线之前将其过滤掉。

具体到CCI总线上,壁垒机制是怎么实现的呢?首先,壁垒和读写一样,也是使用读写通道的,只不过它地址总是0,且没有数据。标志符也是有的,此外还有额外的2根线BAR0/1,表明本次传输是不是壁垒,是哪种壁垒。他是怎么传输的呢?先看弱壁垒,如下图:

在这里插入图片描述

Master0生成了一个数据data,并随后提交了一个弱阻塞请求。在CCI和主设备接口处,一旦接收到阻塞请求,Master0立即执行两项操作:首先,向其上级发送阻塞响应;其次,将阻塞请求转发至与设备Slave0/1相关的接口。Slave1接口能够迅速发送阻塞响应,因为其所在的路径上没有任何未完成的数据传输。然而,Slave0接口无法发送阻塞响应,因为data尚未被发送至该路径,因此在处理阻塞请求时必须保持与写请求的同步顺序。尽管如此,Master0仍能够继续生成第二个数据,因为其所有上级接口(Master0接口)都已经接收到阻塞回应。随后,Master0又发送了一个标志位flag。如图所示:

在这里插入图片描述

当 flag 在 Master0 接口中等待其所有下一级接口的壁垒响应时, Master0 接口在等待所有下一级接口的壁垒响应中被 flag 追踪。当 data 到达 Slave0 后, Master0 接口的壁垒响应被触发, flag 进入下一级处理。在此时,我们无需担心 data 未到达 Slave0,因为 Slave0 接口的壁垒响应在 Master0 接口之前就已经处理完毕。

对于强壁垒指令而言,其主要区别在于Master0接口在接收所有下一级接口的壁垒响应之前,不会发送自身的壁垒响应给Master0。这导致flag无法发送,直到整个壁垒指令的执行完毕。如下图:

在这里插入图片描述

通过采取以下措施,确保了强壁垒完成后,下一条读写指令能够发出请求。此时,强壁垒前的读写指令已经完成。

需要注意的是,ARM架构中的弱壁垒仅针对显式数据访问的顺序。什么是显式数据访问?它包括读写指令、缓存操作以及TLB(Translation Lookaside Buffer)操作。相对地,隐式数据访问指的是处理器层面的操作,例如处理器通过推测执行技术预估指令中的读写操作;缓存系统则具备硬件预取机制,根据之前数据访问的模式自动加载可能需要用到的缓存行。这些操作并未包含在当前指令序列中,因此弱壁垒无法对它们产生影响。切记,弱壁垒只能确保指令顺序的正确性,但无法保证指令之间不存在其他模块对内存的访问,即使这些模块属于同一处理器核心。

就简单来说,如果只是想确保读写顺序,那就用弱壁垒;但如果需要某个读写指令完成之后才能去做别的事情,那就得用强壁垒。这些都适用于普通内存类型。当我们将类型设置为设备时,自动就能保证强壁垒。

我们提到,壁垒专为单核设计。在多核多线程环境中,即使使用了壁垒指令也无法确保读写操作的原子性。解决方法有两种,即软件锁和原子操作。对于原子操作,我们观察到两种实现方式:第一种是当总线收到请求时,立即封锁整个总线,仅允许一个核进行访问。这种方法效率较低。第二种方法是将锁请求转发至对端设备,如内存控制器,从而阻止其他核的访问,而总线仍可正常运行,这种方案效率显著提升,据观察数据显示,操作时间可减少至原来的十分之一。然而,AXI/ACE协议不支持原子操作,因此需要采用软件锁作为替代方案。

软件锁中有个自旋式互斥锁,通过ARM硬件机制中的exclusive access功能来实现互斥。当对一个地址写入值时,相应缓存行会设置一个标记,表明当前无其他核进行写操作。下一条指令读取该行时,若标记未改变,说明写入与读取之间无干扰,从而获取锁。若发生改变,则需返回写入过程重新获取锁。由于缓存一致性机制,该锁变量可被多个核和线程共享。然而,在操作过程中,仍需借助屏障指令来确保操作顺序。

对于普通内存而言,读写操作可能会经过缓存结构,因此无法确定数据是否最终存储在内存中。为了确保数据一致性,通常采用clean操作来完成缓存刷新。然而,clean操作这一概念具有模糊性,因为缓存系统包含多层次结构,包括处理器内部缓存和总线之后的缓存。到底将缓存刷新到何处才算达到最终目标,这一问题尚无明确标准。此外,为了保持数据一致性,刷新操作是否需要通知其他处理器和缓存单元也是一个需要解决的问题。为了解决这些问题,ARM体系提出了Point of Unification/Coherency这一核心概念,并定义了Inner/Outer Cacheable和System/Inner/Outer/Non Shareable等关键特性。

在这里插入图片描述

PoU是指,对于某一个核Master,属于它的指令、数据缓存和TLB,如果在某一点上,它们能够看到一致的内容,那么这个点就是PoU。如图右侧所示,MasterB包含了指令、数据缓存、TLB以及二级缓存。指令、数据缓存和TLB的数据交换都建立在二级缓存上,因此,二级缓存成为了PoU。对于图左侧的MasterA,由于没有二级缓存,指令、数据缓存和TLB的数据交换都建立在内存上,因此,内存成为了PoU。

PoC是指,对于系统中的所有Master节点,如果存在一个点,所有Master节点的指令、数据缓存和TLB都能访问同一个源,那么这个点就是PoC。如图所示,右侧的二级缓存不能作为PoC,因为MasterB超出其覆盖范围,直接访问内存。因此,此时内存成为PoC。在左图中,由于只有一个Master节点,所以内存成为PoC。

进一步分析,如果我们更换右图中的内存为三级缓存,并将其连接到三级缓存之后,那么PoC将变为三级缓存。

基于这两个定义,我们可以明确确定缓存操作和读写指令的发散范围。例如,在下图所示的系统架构中,每组A15控制器包含四个核,每个核内嵌套二级缓存。主存控制器(PoC)位于内存中,而每个A15控制器的用户空间(PoU)则位于其自身的二级缓存中。当在某个A15控制器上执行Clean清指令时,其缓存范围将指定到对应的PoU。显然,四个A15控制器的一级指令缓存全部被清空。那么,其他Master控制器是否受到影响呢?这需要考虑Inner、Outer以及Non Shareable三种类型。

在这里插入图片描述

Shareable属性相对容易理解,它表示某个地址的可能被他人使用。在定义特定页面属性时,我们会明确说明。Non-Shareable属性则仅限于自身使用。需要注意的是,将属性定义为Non-Shareable并不意味着他人完全无法使用。具体来说,如果地址A在核1上被映射为Shareable,在核2上被映射为Non-Shareable,并且两个核通过CCI400相连。那么,当核1访问地址A时,总线会转而监听核2;而当核2访问地址A时,总线可以直接访问内存,无需监听核1。显然,这种处理方式存在明显错误。

就如Inner和Outer Shareable而言,其基本概念在于数据共享属于Outer Shareable。指令以及TLB等只读项的共享属于Inner Shareable,同时这些共享也涉及Outer Shareable。在上图所示的系统架构中,A15和A7共享指令行为,这种共享关系属于Inner Shareable。当然,它们数据共享特性使其也符合Outer Shareable的标准。相比之下,DMA、Mali和处理器之间仅共享数据,因此它们属于Outer Shareable,而不属于Inner Shareable。

在集成A15/A7等ARM处理器到SoC系统的过程中,需要对处理器的Inner和Outer Shareable属性进行配置。若选择配置为Inner Shareable,将仅限读取的指令操作、指令缓存、TLB等数据会被发送至总线。若选择配置为Outer Shareable,将仅限读取的数据操作、数据缓存的访问会被发送至总线。需要注意的是,若选择配置为Outer Shareable,必须同时配置为Inner Shareable。反之,若不选择Outer Shareable,则无需特别配置Inner Shareable。通常情况下,建议同时配置这两个属性以确保系统的正常运行。

这些概念可能让你好奇它们的实际用途是什么?回到上文的Clean指令,PoU操作会清除四个A7指令缓存中的相关行。由于Inner Shareable属性,这个操作会被扩散到总线。而CCI400总线会将该操作广播到所有可能接受的口。ACE口首优先处理,导致四个A15的指令缓存行也被清除。对于Mali和DMA控制器,它们属于ACE-Lite类型,因此无需进行清空操作。不过实际上,这些控制器没有指令缓存,因此它们只能被动接收请求,而不会执行任何操作。

你或许会疑惑,为何我们对缓存维护和读写指令的范围进行如此详细的定义?其作用在于,精确限定缓存维护和读写指令的作用范围。若调整策略,总线将无法执行Inner/Outer Shareable的广播机制,这将导致仅有A7处理器组负责清除缓存行。这种做法在逻辑上存在缺陷,因为A7和A15处理器可能同时执行相同的代码。如前所述,若将读写属性设置为Non-Shareable,总线将不再关注其他主设备,从而减少访问延迟,这种设计方式能够显著提升性能水平。

回到前面的问题,如何确定数据最终是否写入内存中?很遗憾,我们无法直接确认这一点。你只能将范围设置为PoC。如果PoC为三级缓存,最终数据将停留在三级缓存;若为内存,则停留在内存。根据定义,如果所有Master的缓存数据统一存放在三级缓存中,则无需刷至内存。

简单来说,PoU/PoC参数确定了指令和命令能够到达的缓存或内存位置。当指令和命令到达指定位置后,Inner/Outer Shareable参数则决定了它们的广播范围。

让我们深入理解Inner/Outer Cacheable这一概念,这一概念相对简单,仅限于缓存的层级划分。其中,一级缓存始终是Inner Cacheable,而最外层缓存,如三级缓存,既可以是Outer Cacheable,也可以是Inner Cacheable。它们的作用在于,当定义内存页属性时,能够在不同缓存层次上实施不同的处理策略。

在ARM处理器和总线手册中,也会包含几个PoS(Point of Serialization)的概念。它的作用是确保所有来自主设备的请求类型都能被控制器接收和处理。当出现冲突时,这些请求就会以串行方式处理,以避免数据争执。这个概念与其他相关技术则保持独立。

在总线的演变过程中,一个关键问题尚未被充分讨论,即动态规划重排与合并操作。处理器和内存控制器均配置了相同的模块,负责将所有的传输进行分类、合并、顺序调整,并预测可能的读写请求地址,以实现传输的最大效率。在性能分析中,这一问题将再次被提及。然而,在总线层面上,软件的影响相对较小。明白了总线延迟和OT的优化能够与性能计数器的统计结果精确匹配,看看是否达到了预期。

全部评论 (0)

还没有任何评论哟~