AxCACHE[0]:Bufferable
表示该事务在到达最终目的地之前,是否可以被中间节点(如缓存、写缓冲区)暂存。
情况一:写事务
规则:
:写响应表明数据已到达最终目的地。
Bufferable = 0
:写响应可以从一个中间点发出,只要满足了“可观测性”要求。
Bufferable = 1
举例说明:
场景A: (Non-bufferable)
Bufferable = 0
行为:像一个同步写或一个“发布”操作。
例子:CPU 要写入一个外设的“命令寄存器”来启动一个 DMA 传输。它设置 。
AWCACHE[0] = 0
CPU 发出写事务,并等待响应。
系统中的互联结构和缓冲区必须让这个写事务“穿透”所有中间环节,直到它被最终的外设控制器接收并处理。
只有当外设控制器真正处理了这个命令后,它才会返回一个写完成响应(BRESP)。
CPU 收到这个响应后,才知道 DMA 确实已经启动,然后才能安全地执行后续依赖于 DMA 的操作。
关键:响应来自最终目的地,保证了数据在返回响应时已经生效。
场景B: (Bufferable)
Bufferable = 1
行为:像一个异步写。
例子:CPU 要向一片内存写入大量数据。它设置 。
AWCACHE[0] = 1
CPU 发出写事务。
这个写事务很快被一个写缓冲区 接收。写缓冲区立即返回一个写完成响应(BRESP)给 CPU。
CPU 收到响应后,就可以立刻继续执行其他任务,而不必等待数据缓慢地写入主内存。
之后,写缓冲区在后台负责将数据最终写入主内存。
“可观测性”要求:这个中间点(写缓冲区)必须保证,任何后续能看到该数据的主设备(如另一个CPU核心或DMA),都必须从它这里或者最终目的地看到最新的数据。它不能简单地“丢弃”数据。这通常意味着它必须拥有将数据最终推送至共享主内存的能力。
情况二:读事务(特定条件:Non-cacheable & Modifiable)
ARCACHE[3:2] 被置低(不可缓存)且 ARCACHE[1] 被置高(可修改)的读取事务
规则:
:读数据必须来自最终目的地。
Bufferable = 0
:读数据可以来自最终目的地,或者来自一个正在进行的、前往最终目的地的写操作。
Bufferable = 1
举例说明:
场景A:
Bufferable = 0
行为:强制读穿透。
例子:CPU A 刚写完数据,CPU B 要读取这个数据。设置 为
ARCACHE。
Non-cacheable, Modifiable, Bufferable=0
CPU B 发出读请求。
即使 CPU A 之前发出的写数据还在某个写缓冲区里“行进”,系统也必须确保 CPU B 的读请求“绕过”或“等待”这个缓冲区,直接从主内存中读取数据。
这保证了 CPU B 能看到所有之前已到达主内存的数据,实现了严格的内存一致性。
场景B:
Bufferable = 1
行为:允许读合并。
例子:CPU A 向内存写入数据,该数据还在一个系统级的写缓冲区中。紧接着,CPU B 要读取同一个地址。
如果 CPU B 的读事务是 ,那么系统可以允许 CPU B 的读请求直接从那个写缓冲区中获取数据,而不是去读主内存中已经过时的旧数据。
Bufferable=1
这提升了对共享数据访问的效率,避免了读操作去访问慢速的主内存。
情况三:其他 ARCACHE 组合
规则:对于其他组合,Bufferable 位无影响。
解释:这通常是指可缓存 的读事务。如果一个读事务被标记为可缓存的,那么数据来源将由缓存一致性协议来管理,而不是由 Bufferable 位来控制。缓存本身就是一个巨大的、结构化的缓冲区,它的存在已经隐含了复杂的缓冲和合并行为,因此单独的 Bufferable 位在此场景下不再适用。
这种精细分类的核心原因是区分 “生产者-消费者” 模型和 “数据填充” 模型,并平衡 性能 与 一致性/正确性。
保证与外设交互的正确性
对寄存器(命令、状态、数据寄存器)的访问必须是精确的。一个“启动”命令必须在收到响应时确实已生效。一个状态寄存器的读取必须反映外设的真实状态。 是实现这种精确性的硬件机制。它确保了操作序列的严格顺序。
Bufferable=0
最大化内存访问性能
对大片内存的写入(如填充帧缓冲区、计算中间结果)不要求每次写入都立即生效,只要求在需要共享或持久化之前最终生效即可。 通过引入写缓冲区极大地隐藏了写入延迟,释放了处理器,是提升系统吞吐量的关键技术。
Bufferable=1
实现高效的数据共享与一致性
读事务中的 Bufferable 位规则,是为了在弱内存序的系统中提供一个可控的一致性模型。它允许系统设计者选择:
强一致性():总是读最终目的地,简化软件编程模型,但性能较低。
Bufferable=0
弱一致性():允许从中间点读,性能更高,但要求软件在需要时使用明确的内存屏障 指令来强制同步。
Bufferable=1
这些规则的诞生源于计算机架构中一个经典且持续存在的矛盾:处理器速度与内存速度的不匹配,以及多主设备系统中的数据共享。
写缓冲区的普及
为了解决 CPU 等主设备速度远快于内存和慢速外设的问题,写缓冲区成为了标准设计。但引入缓冲区后,带来了“写操作何时真正完成”的问题。AXI 协议通过 位将这个选择权交给了软件/系统架构师,让他们根据访问对象的不同来决定是否使用缓冲区。
Bufferable
复杂 SoC 的兴起
在拥有多个 CPU、GPU、DMA 的复杂 SoC 中,数据可能在多个地方存在副本(缓存、写缓冲区)。这产生了缓存一致性和内存一致性问题。
背景:需要一个明确的协议来定义不同主设备何时能看到彼此的数据。
位就是 AXI 协议中用于控制写传播 和读来源 的基本原语。它定义了事务在系统中的“可见性”边界。
Bufferable
对弱内存序模型的硬件支持
现代高性能处理器普遍采用弱内存序来提升性能。这意味着事务的完成顺序可能与发出顺序不同。
AXI 协议本身不强制严格的全局顺序。 和
Bufferable 等属性正是实现这种弱内存序的关键工具。协议通过将这些属性暴露给软件,让操作系统和驱动程序开发者能够在需要强顺序的地方(如外设访问)通过设置
Modifiable 来强制顺序,而在不需要的地方(如内存写入)通过设置
Bufferable=0 来获取高性能。
Bufferable=1
AxCACHE[1]:Modifiable
当 AxCACHE[1] 置1时,事务为可修改 (Modifiable),表示事务的特性可以被修改。 AxCACHE[1] 置0时,事务为不可修改 (Non-modifiable)。 以下部分描述了不可修改和可修改事务的属性:
Non-modifiable transactions
核心规则:事务在传输过程中,其基本特征绝对不能被任何中间组件(如互联、缓冲区或缓存)改变。它必须“原样”执行。
被固定的参数(Table A5.2):
AxADDR:起始地址不能变。
AxLEN:突发传输的长度(拍数)不能变。
AxSIZE:每拍数据的大小(字节数)不能变。
AxBURST:突发类型(如增量、回环)不能变。
AxPROT/AxNSE:访问权限和安全属性不能变。
举例:访问内存映射外设寄存器
场景:CPU 需要配置一个 UART(串口)控制器。它需要执行以下操作:
向控制寄存器(地址 )写入
0x4000_1000 以启用发送和接收。
0x03
向波特率寄存器(地址 )写入
0x4000_1004 以设置波特率为 115200。
0x68
行为:这两个写事务必须被标记为 。
Non-modifiable
系统不能将这两个对相邻地址的写入合并成一个两拍的突发写入。因为硬件设计上,写入控制寄存器会触发一个状态机复位,而写入波特率寄存器会重新配置内部时钟分频器。如果合并,硬件可能无法正确识别这两个独立的命令,导致 UART 无法工作。
同样,系统也不能将一个对控制寄存器的单次写操作拆分成两个更小的写操作。
关键:对于外设,每次访问都可能有一个独立的、不可分割的副作用,因此必须保持其原子性和精确性。
除此之外,不可修改事务中一些没被固定的参数是可以修改的,但是要遵循一定的规则:
1. 仅允许 Bufferable 到 Non-bufferable 的转换
规则: 属性只能从
AxCACHE 改为
Bufferable,禁止反向或其他修改。
Non-bufferable
举例:一个事务从某个主设备发出,标记为 。当它经过系统中的一个内存屏障单元 或一致性控制器 时,该单元可能认为这个数据必须被立即持久化(例如,在同步点之前),于是它将事务的
Bufferable 位清零,使其变为
Bufferable。
Non-bufferable
禁止反向的例子:你不能将一个起始为 的写事务(如写入一个外设命令寄存器)改为
Non-bufferable,因为这可能导致该命令被缓冲在某个中间节点,无法及时生效,从而破坏硬件的正确性。
Bufferable
2. 超长 Non-modifiable 事务的拆分
规则:即使是一个 事务,如果其突发长度(
Non-modifiable)超过 16,也允许被拆分成多个较小的突发事务。
AxLEN
举例:一个 DMA 控制器发起一个 的读事务,长度为 32(
Non-modifiable),从一个外设的 FIFO 读取数据。但是,目标外设或互联结构可能有一个硬件限制,单次突发处理能力最大为 16。
AxLEN=31
行为:系统可以将这个 32 拍的突发拆分成两个 16 拍的 突发事务。每个新事务的起始地址都根据拆分点正确计算(例如,第二个事务的地址是起始地址 + 16 * 数据宽度)。
Non-modifiable
关键:虽然长度和单个事务的地址被修改了,但访问的总体字节序列和顺序没有改变。这对于从 FIFO 读取数据是至关重要的,顺序绝对不能错。
3. 独占访问事务的修改
规则:对于标记为独占访问( 信号有效)的
AxLOCK 事务,允许修改
Non-modifiable(每拍大小)和
AxSIZE(长度),但条件是访问的总字节数必须保持不变。
AxLEN
举例:CPU 发起一个独占读,目的是监控一个 16 字节的内存区域。它使用 (4字节,即 32位),
AxSIZE=4(共 4 拍,4*4=16字节)。
AxLEN=3
行为:如果系统中的一个桥接器或互联结构的数据通路是 64 位(8字节)宽的,它可能会将这个事务修改为 (8字节),
AxSIZE=3(共 2 拍,2*8=16字节)。
AxLEN=1
关键:尽管事务的形态变了,但它监控的内存范围完全一致。这对于保持独占访问的语义至关重要,因为独占访问的成败取决于监控的特定地址范围是否被其他主设备修改。
4. 应对硬件限制的强制修改
规则:当遇到无法满足 要求的硬件限制时(如数据宽度不匹配),事务必须被修改。执行此操作的组件可以提供一个“实现定义”的机制来指示修改已发生。
Non-modifiable
举例:一个主设备发起一个 写事务,
Non-modifiable(4字节写入),目标是数据总线宽度仅为 16 位(2字节)的外设。
AxSIZE=2
行为:系统无法在不修改事务的情况下完成它。一个桥接器必须将这个 4 字节的单次写拆分成两个 2 字节的写事务。
调试机制:高级的调试工具可能会在桥接器中设置一个状态位,当发生这种“强制修改”时,该位被置起。这样,软件工程师在调试时就能知道,总线上实际发生的事务与 CPU 发出的原始指令并不完全一致,从而帮助定位一些非常隐蔽的硬件-软件交互问题。
不可修改事务中上述这些例外规则的存在,源于理论上的理想模型与物理世界中的实际硬件限制之间的冲突。
安全性与性能的权衡
只允许 Bufferable -> Non-bufferable:这是一个安全性 措施。将事务“收紧”总是安全的(确保数据到达终点),而将其“放松”则可能破坏正确性。这保证了系统在优化性能(使用缓冲区)的同时,永远有一个“紧急制动”可以让关键数据强制到达目的地。
处理物理现实
允许拆分超长突发:硬件组件(如内存控制器、外设)的缓冲区深度是有限的。协议不能假设所有组件都能处理任意长度的突发。这个例外是实用性 的体现,它确保了协议可以在各种不同能力的硬件组件之间互操作。
保持高级语义的本质
允许修改独占访问的 SIZE/LEN:独占访问的本质是监控一个连续的内存区域。只要这个区域不变,具体用几个时钟周期、以何种数据位宽来传输,是实现细节。这个例外在保持独占访问核心语义不变的前提下,赋予了硬件实现上的灵活性,以优化数据传输效率。
保证互操作性和可调试性
强制修改和调试机制:这是对现实世界复杂性的终极妥协。当协议规则与物理定律(如总线宽度)冲突时,必须修改事务以保证功能正常。同时,提供一个可选的调试机制是工程智慧的体现。它承认了这种修改可能会引入难以察觉的副作用,并为系统集成者和软件开发者提供了追查问题的线索。
这些例外规则的诞生,是 AXI 协议在经历了实际芯片设计和系统集成挑战后,不断演进和成熟的结果。
复杂 SoC 集成的经验教训
在集成来自不同供应商的 IP 核时,设计者发现,严格坚持 规则有时会导致系统无法工作。例如,一个 IP 核可能发出一个很长的
Non-modifiable 突发,而另一个 IP 核无法处理。最初的严格规则在实践中遇到了瓶颈,迫使协议增加例外条款来保证系统的可集成性。
Non-modifiable
对缓存一致性和同步原语的深入理解
随着多核处理器的普及,对 ARM 的独占访问(用于实现原子操作)这类同步原语的理解更加深入。协议制定者意识到,对于独占访问,重要的是保护的内存范围,而不是传输的具体形式。因此,为了优化多核系统总线利用率,允许对独占访问事务进行无损修改就成了必然选择。
调试成本的驱动
在早期,当一个事务在总线深处被默默修改后,软件行为异常,工程师可能需要花费数周时间用逻辑分析仪去捕捉总线信号才能定位问题。这种高昂的调试成本催生了对“事务修改指示”这类可观测性 功能的需求。将其作为“实现定义”的特性,既为高端调试提供了可能,又没有给所有设计增加强制性负担。
Modifiable transactions
核心规则:允许系统为了优化性能而改变事务的特征。最常见的是事务的合并与拆分。可修改事务可以通过以下方式进行修改:
1. 拆分事务
规则:一个大的事务可以被拆分成多个较小的事务。
举例:一个 CPU 发起一个长度为 16 拍的读突发,从一个 DDR 内存读取数据。但是,内存控制器当前的处理队列已满,无法立即处理整个突发。于是,互联结构可以将这个突发拆分成两个 8 拍的读事务,依次发送给内存控制器。这避免了整个事务被阻塞,提高了系统的响应性。
2. 合并事务
规则:多个小事务可以被合并成一个大的事务。
举例:这是最重要的优化。假设一个 DSP 核心需要向帧缓冲区的连续地址写入 4 个 32 位像素数据。它发出了 4 个独立的写事务(地址分别为 A, A+4, A+8, A+12)。
行为:系统中的一个写缓冲区探测到这些事务是连续的、大小相同且可修改。它将这些事务合并成一个单一的 4 拍突发写入,起始地址为 A,突发类型为 INCR。
好处:总线上的事务数量从 4 个减少到 1 个,极大地减少了地址相位和控制相位的开销,总线带宽几乎全部用于有效数据传输,性能显著提升。
3. 读事务预取
规则:一个读事务可以获取比所需更多的数据。
举例:CPU 需要从地址 读取 4 字节的数据(
0x8000)。如果系统判断这个访问模式是顺序的,并且数据通路是 64 位的,它可能会直接执行一个 8 字节的读取(
AxSIZE=2),从地址
AxSIZE=3 读回 8 个字节。
0x8000
后续:多读取的 4 字节数据(来自地址 )可以被存入一个预取缓冲区。当 CPU 下一次访问
0x8004 时,数据已经可用,实现了零等待状态的读取。
0x8004
关键:这本质上是硬件控制的缓存行填充。
4. 写事务的字节使能保护
规则:一个写事务可以访问一个更大的地址范围,但使用 信号来确保只有需要的位置被更新。
WSTRB
举例:CPU 要执行一个 4 字节的写操作到地址 。但目标从设备的数据总线是 64 位(8字节)宽的。系统可以将这个事务转换为一个对起始地址
0x9002 的 8 字节访问(
0x9000),但将
AxSIZE=3 信号设置为
WSTRB(假设小端序)。
0b00111100
解释:这个 意味着只有中间 4 个字节(对应原始目标地址
WSTRB 到
0x9002)被实际更新,而地址
0x9005 和
0x9000-0x9001 的内容保持不变。
0x9006-0x9007
好处:允许窄带写入高效地适应宽带数据通路,而无需额外的读-修改-写操作。
5. 允许修改的核心参数
规则:,
AxADDR,
AxSIZE,
AxLEN 都可以被修改。
AxBURST
举例:结合上述所有例子,修改这些参数是实现拆分、合并、预取和宽度适应的基础。
6. 绝对禁止修改的属性
规则: 和
AxLOCK 绝对不能被改变。
AxPROT
举例:
:如果一个事务是独占访问(用于实现原子操作),它必须始终保持其独占属性。如果系统在优化过程中将其改为普通访问,将彻底破坏原子操作的语义,导致数据竞争和系统错误。
AxLOCK
:这些信号定义了事务的安全状态(安全 vs 非安全)、权限级别(特权 vs 用户)和访问类型(指令 vs 数据)。修改这些会破坏系统的安全边界和内存保护。例如,一个来自非安全域的用户模式访问绝不能通过修改属性来获得访问安全区域或特权寄存器的能力。
AxPROT
分成上述六类的原因,在于设计的核心原因是在追求极致性能的同时,坚守系统正确性和安全性的底线。
最大化性能
属性存在的首要目的就是性能。通过拆分、合并、预取,系统可以:
Modifiable
减少总线开销(合并)。
提高响应能力(拆分)。
隐藏内存延迟(预取)。
适应不同的数据路径(修改 SIZE)。
这直接解决了处理器与内存之间的速度鸿沟和总线瓶颈问题。
保证系统正确性与稳定性
锁定 :独占访问是构建锁、信号量等同步原语的硬件基础。破坏其原子性会导致多核系统崩溃和数据损坏。这是不可逾越的正确性红线。
AxLOCK
锁定 :这是系统安全性和稳定性的基石。它确保了用户程序不能越权访问操作系统内核的数据,非安全世界的应用(如一个普通APP)不能访问安全世界的资源(如指纹数据)。修改这些属性会摧毁现代计算系统最基本的安全隔离和保护机制。
AxPROT
实现硬件优化的灵活性
通过明确划定“什么能改”和“什么不能改”,AXI 协议为硬件设计者提供了一个清晰的框架。他们可以大胆地为“能改”的部分设计激进的优化器(如复杂的合并缓冲区),而无需担心会无意中触碰到“不能改”的禁区,从而危及系统功能。
同时这些规则的诞生是现代计算系统架构演进的直接结果,尤其受到以下因素的驱动:
对总线效率的极致追求
在早期总线中,每个事务都有固定的开销。随着处理器核心增多,总线竞争加剧,效率低下的问题被放大。行业需要一种标准化的方法来消除这些低效操作。 事务就是对这一需求的直接回应,它将“事务合并”等优化技术从可选的实现细节提升为标准化的协议特性。
Modifiable
多核处理与并发编程的普及
多核处理器的出现使得硬件级的同步原语(如原子操作)变得至关重要。AXI 的独占访问()就是 ARM 架构对此的响应。因此,协议必须无条件地保护这些机制的完整性,否则整个多核软件栈将无法正常工作。这条规则的背景就是并发编程的硬件需求。
AxLOCK
可信执行环境与系统安全
随着移动支付、数字版权管理等应用的发展,在单个 SoC 内划分“安全世界”和“非安全世界”的 TrustZone 技术变得普及。 信号是实现这种隔离的关键硬件信号之一。协议强制规定其不可修改,反映了行业对硬件辅助安全的深刻认同和严格保障。它的背景是日益严峻的网络安全威胁和对系统级安全性的高标准要求。
AxPROT
虽然在AXI协议中是拥有者可修改的功能,但 属性的修改并非随心所欲,核心原则是:任何修改都不能削弱事务对其他组件的可见性,也不能改变其原本的缓存查找需求。源于以下几个方面的考量:
AxCACHE
应对处理器与内存的速度鸿沟:通过允许合并、拆分等优化手段,减少总线事务数量,隐藏内存访问延迟,从而提升系统整体吞吐量和效率。
维护多主设备系统中的数据一致性:在拥有多个CPU、DMA等主设备的复杂SoC中,必须有一套明确的规则来管理事务的传播和缓存行为,确保所有主设备对共享数据有一致的视图。 修改规则与属性匹配规则共同作用,来维护这种一致性。
AxCACHE
保证硬件交互的正确性与安全性: 等绝对禁止修改的信号,关系到系统的安全状态和访问权限。4KB边界规则则与内存管理单元(MMU)的页面组织方式协同工作,确保内存保护属性的有效性。这些规则共同为系统提供了一个可靠的安全基础。
AxPROT
| 规则类别 | 核心规定 | 举例说明 | 主要原因 |
|---|---|---|---|
| 允许的修改 | 属性本身:通常只允许从”Bufferable”改为”Non-bufferable”。 |
事务经过一个内存屏障单元时,为确保关键数据持久化,将其从可缓冲改为不可缓冲。 | 强化可见性:确保关键操作(如DMA启动命令)的数据到达最终目的地,避免停留在中间节点。 |
事务形态:对于标记为 的事务,可以拆分、合并,或调整地址、长度等。 |
多个小的写事务合并为一个大的突发写入;一个大的读事务拆分成多个较小的读事务。 | 提升总线效率:减少事务开销,提高带宽利用率,适应不同组件的处理能力。 | |
| 事务ID和QoS值:可以被修改。 | 互联结构在转换事务时,可能分配新的ID以管理乱序完成。 | 系统优化:支持事务ID管理和服务质量调整。 | |
| 禁止的行为 | 跨越4KB地址空间:修改后的事务不能访问与原事务不同的4KB地址空间。 | 原事务访问 至 范围,修改后的事务不能触及 及以后的地址。 |
保持地址边界:防止破坏基于4KB页面组织的内存保护区域属性。 |
| 破坏单拷贝原子性:对单拷贝原子性大小区域的访问,不能拆分成多次访问。 | 对单个对齐的32位变量(在支持32位原子性的系统中)的写入,必须在一个事务内完成,不能拆成两个16位写入。 | 保证原子操作:确保基本的原子读写操作,为软件提供必要的同步原语基础。 |
Non-modifiable transactions和modifiable这样分类的原因
这种分类的根本原因是在 性能 和 硬件正确性 之间建立一个清晰的界限。特别是内存映射I/O的普及和 高性能总线的需求。
内存映射I/O成为主流
在早期计算机中,I/O 可能有独立的地址空间和指令(如 x86 的 指令)。但现代处理器,特别是 RISC 架构和嵌入式系统,普遍采用内存映射I/O,即 CPU 使用普通的加载/存储指令来访问外设。
IN/OUT
带来的问题:总线如何区分一次内存访问是访问“普通内存”还是访问“有特殊行为的外设”?
解决方案:需要一种机制来标记这些访问。 就是为内存映射I/O访问定义的“特殊行为”标志。它告诉总线:“这不是普通的内存访问,别乱动它”。
Non-modifiable
处理器与内存的速度鸿沟
处理器速度远快于内存访问速度。为了填补这个鸿沟,系统采用了缓存、写缓冲区和更宽的总线等技术。
总线成为瓶颈:在 SoC 中,总线本身也是一个共享资源,其带宽和效率至关重要。单个事务的地址相位、控制相位和响应相位都会消耗时间和资源。
解决方案:为了最大化总线利用率,必须能够将多个小事务合并成少量的大事务。 属性就是对普通内存访问开启这扇优化大门的钥匙。没有它,总线将被海量的小型单次访问淹没,系统性能将大打折扣。
Modifiable
复杂 SoC 互联的需求
现代 SoC 包含数十个主设备和从设备。一个标准的互联结构(如 ARM 的 NIC-400)需要服务来自各处的请求。
背景:需要一套清晰的“交通规则”来管理数据流。
和
Modifiable 就是这套规则的重要组成部分。它们允许互联结构智能地管理事务流,在保证关键外设访问正确性的同时,对普通内存数据流进行积极的优化和整形,从而实现系统整体性能和正确性的最优平衡。
Non-modifiable
例如在传输过程中,事务的属性是否可以被改变以优化性能。最常见的变化是允许将多个单次访问合并成一个突发传输,或者将一个大突发传输拆分成多个小的。一个简单的例子:CPU 需要连续写入 4 个 32 位数据到相邻的地址。如果设置了 ,系统中的一个互联组件或缓存可以探测到这 4 次独立的写请求,然后将它们“合并”成一个 4 拍的突发写入。这大大减少了总线上的事务开销,提高了总线利用率和效率。但并不是每种事务都可以这么随心所欲的被修改,例如反例:
Modifiable=1 意味着必须“原样”执行事务,不能做任何合并或拆分。这对于访问内存映射外设寄存器至关重要,因为对某些寄存器的每次写入都可能触发一个特定的硬件动作,合并写入会改变硬件行为,导致错误,但总归来讲,可以分为以下几点原因:
Modifiable=0
保证与内存映射外设交互的正确性
这是设立 类别最主要、最直接的原因。外设寄存器不是普通的存储单元。对它们的访问是向硬件发送“命令”或读取“状态”。每个命令都必须是精确和独立的。合并或拆分访问会彻底改变这些命令的语义,导致硬件行为异常。
Non-modifiable 强制总线遵守程序员的原始意图,确保硬件驱动能够正确工作。
Non-modifiable
最大化对普通内存的访问效率
对于普通的 DDR 内存、SRAM 或片上 RAM,数据本身没有副作用。访问的唯一目的就是读取或写入数据本身。因此,性能是首要目标。
属性解锁了关键的优化手段:
Modifiable
合并 减少了总线事务的管理开销,是提升写操作效率的最重要技术之一。
拆分 提高了系统对不同带宽和延迟组件的兼容性,防止大型事务阻塞总线。
简化系统设计
通过属性信号明确标识事务的类型,系统组件(如互联开关)无需猜测一个事务是否可以优化。它可以根据 位做出明确的决定:遇到
AxCACHE[1] 事务就积极优化;遇到
Modifiable 事务就原样转发。这使互联结构的设计更加模块化和可靠。
Non-modifiable
AxCACHE[3:2]:Allocate 和 Other Allocate
含义:这两个位共同为缓存提供建议,指示是否应该为该事务分配缓存行。
Allocate:建议在本级缓存中分配。
Other Allocate:建议在其他级缓存(如下一级缓存)中分配。
这两个位共同为缓存提供指导,告诉缓存应该如何对待当前访问的数据。它们本质上是对缓存行为的“建议”,而非强制命令。缓存可以根据自身策略选择是否遵循,通过一个多级缓存系统(如 L1 缓存和 L2 缓存)的例子来理解:
情况一:
Allocate 位有效(建议在当前级缓存分配)
Allocate
含义:
必须查找:数据可能已经在缓存里了,所以必须在缓存中查找对应的缓存行。
建议分配:如果数据不在缓存里(即缓存未命中),建议将其分配到本级缓存中,以备将来使用。
举例:CPU 核心频繁访问的代码和数据
场景:CPU 正在执行一个循环,反复操作一个数组。它发起读请求来获取数组元素。
行为:CPU 会设置 的
ARCACHE(并且
Allocate=1)。
Other Allocate=0
当读请求到达 L1 缓存 时,L1 缓存会首先查找请求的地址。
如果未命中,L1 缓存很可能会从 L2 缓存或主内存获取数据,并分配一个缓存行来存储它。因为这符合“将来会再次访问”的预期。
结果:数据被保存在最靠近 CPU 的 L1 缓存中,后续的访问会以极高的速度命中。
情况二:
Other Allocate 位有效(不建议在当前级缓存分配)
Other Allocate
含义:
必须查找:数据同样可能已经在缓存里,所以必须在缓存中查找。
不建议分配:如果数据不在缓存里,不建议将其分配到本级缓存中,因为预计它不会被再次访问。
举例:DMA 传输的大量数据(流数据)
场景:一个网卡 DMA 正在将接收到的网络数据包写入内存。CPU 需要读取这个数据包进行处理,但这个数据包很大且通常只处理一次。
行为:CPU 会设置 的
ARCACHE(并且
Other Allocate=1)。
Allocate=0
当读请求到达 L1 缓存 时,L1 缓存仍然会执行查找(因为数据可能因某些原因已经在里面了)。
但如果未命中,L1 缓存不会为这些数据分配新的缓存行。它可能会直接从 L2 缓存或主内存将数据返回给 CPU,然后丢弃它。
同时,这个“不在本级分配”的建议可能会传递给 L2 缓存。L2 缓存看到 和
Allocate=0,它自己也可能选择不分配,或者只做短期保留。
Other Allocate=1
结果:宝贵的 L1 和 L2 缓存空间没有被这些“一次性”的数据所污染,从而保证了高频访问的代码和数据能留在缓存里。这被称为缓存友好的设计。
情况三:
Allocate 和
Other Allocate 均无效(不可缓存)
Allocate
Other Allocate
含义:请求不要求在任何缓存中查找。
举例:访问内存映射的外设寄存器
场景:CPU 读取一个 UART 的状态寄存器,查看是否有新数据到达。
行为:。
ARCACHE[3:2] = 0b00
缓存不会为这个地址执行查找操作。请求会直接绕过缓存,发送到外设总线上。
原因:外设寄存器的值可能会在两次访问之间由硬件自行改变。如果被缓存,CPU 读到的将是缓存的旧副本,而不是真实的寄存器状态,导致程序行为错误。
这种精细分类的核心原因是高效地管理稀缺的缓存资源,并在多级缓存系统中提供明确的指导。
防止缓存污染
这是 位存在的首要原因。系统需要一种机制来标识那些具有“流”特性或一次性访问的数据。如果没有它,诸如视频帧数据、DMA 缓冲区这类大型、一次性访问的数据会迅速将更有价值的热点数据(如程序代码)从缓存中挤出,导致缓存抖动,性能急剧下降。
Other Allocate
优化缓存层级结构
在多级缓存中,数据放在哪一级对性能和功耗有巨大影响。
表示:“请考虑把我放在离主设备最近的高速缓存里”。
Allocate=1
表示:“我可能在其他地方(如下一级缓存或主内存)还有用,但别把我放在你这里”。
Other Allocate=1
这为缓存控制器提供了智能分配数据的依据,从而实现整体性能最优。
维持数据一致性
“必须查找” 的要求至关重要。即使不分配新行,缓存也必须检查数据是否已经存在于其中。如果存在,缓存必须提供最新的数据副本(对于读)或使旧副本失效(对于写)。这是维护缓存和主内存之间数据一致性的基础,特别是在多核系统中。
区分内存类型
这种分类是区分 (可缓存,可使用 Allocate 位)和
Normal Memory(通常不可缓存,Allocate=0, Other Allocate=0)行为的具体体现。
Device Memory
这些规则的诞生直接源于多级缓存层次结构和多核处理器的普及。
缓存层级加深
随着处理器速度与内存速度的差距拉大,单一的 L1 缓存已不足以解决问题。L2、L3 甚至 L4 缓存变得常见。
背景:需要一种机制来指导数据在 L1/L2/L3 缓存中的存放位置。简单的“可缓存”或“不可缓存”的二元选择已经不够用了。 和
Allocate 提供了这种更精细的控制粒度,允许系统架构师为不同的内存区域(如代码区、堆区、帧缓冲区)定义不同的缓存策略。
Other Allocate
多核与一致性协议
在多核处理器中,每个核心都有自己的私有缓存,它们通过缓存一致性协议(如 MESI)来同步。
背景:“必须查找” 的要求与这些一致性协议紧密相关。无论分配建议如何,缓存都必须参与查找,以确定该地址的缓存行在其他核心的缓存中是否处于“共享”、“独占”或“修改”状态,从而采取正确的行动。这是维持多核系统全局数据一致性的硬性要求。
大数据与流处理工作负载
现代工作负载(如网络数据包处理、视频编解码、科学计算)涉及大量顺序访问且重用性低的数据。
背景:传统的缓存设计假设“时间局部性”和“空间局部性”,但对于流数据,只有很强的空间局部性,几乎没有时间局部性。 位就是为这类工作负载量身定制的,它允许硬件利用数据的空间局部性进行预取等操作,同时又避免因为分配缓存行而造成污染。
Other Allocate
注意: 和
Allocate 在读写通道的位置是互换的。这是一个协议设计细节,软件驱动通常通过一个统一的接口来配置内存属性,硬件会自动处理这种位序差异。
Other Allocate
