• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

内存类型Write-through Write-allocate详解

好的,我们来深入解析 AXI 协议中一个相对特殊但设计精巧的内存属性:Write-Through Write-Allocate。

这个组合初看可能有些反直觉,因为 Write-Through 和 Write-Allocate 似乎追求的是两个相反的目标(一个强调立即写内存,一个强调用缓存缓冲写操作)。但正是这种组合,解决了特定场景下的性能瓶颈。

核心结论先行

  • 是什么: Write-Through Write-Allocate 是一种缓存策略。Write-Through 指写入时同时更新缓存和主内存;Write-Allocate 指在写未命中时,先将数据所在的内存块载入缓存,然后再执行写入。
  • 目标: 优化对尚未被缓存的、需要强一致性的、并且后续很可能被读取的数据的首次写入性能。
  • 本质: 通过 Write-Allocate 将“写未命中”转化为“写命中”,从而使得后续操作能享受到缓存带来的速度优势,同时通过 Write-Through 维持数据的全局可见性。

深入解析:拆解与组合

1. Write-Through (透写)

  • 核心行为: 所有写入操作(无论命中与否)都必须最终更新到主内存。如果是缓存命中,则同时更新缓存和内存。
  • 设计目标: 强数据一致性。保证其他系统主设备(如DMA、其他CPU核心)能立即看到写入的结果。

2. Write-Allocate (写分配)

  • 核心行为: 当发生写未命中时(要写入的数据不在缓存中),CPU 不会直接写入内存。而是会:
    1. 在缓存中分配一个新的缓存行。
    2. 将整个缓存行的数据从主内存加载到缓存中(即使你只想写其中一个字)。
    3. 然后,在缓存中更新目标位置的数据。
  • 设计目标: 利用空间局部性原理。假设如果你要写一个数据,你很可能会很快再次写入或读取它附近的数据。提前加载整个缓存线,为后续的访问提速。

3. Write-Through Write-Allocate 的组合效应

这个组合的关键在于处理 “写未命中” 的场景。让我们对比一下不同策略的处理流程:

场景: CPU 要执行一个 32 位写操作(STR)到地址 0x1000,但该地址所在的 64-byte 缓存行当前不在缓存中。

  • 采用 Write-Through No-Allocate:

    1. 数据 (0x1000 处的 4 个字节) 被直接写入主内存。
    2. 操作结束。缓存无变化。
    • 结果: 写入完成,一致性得到保证。但如果 CPU 马上要读 0x1004,会发生读未命中,又需要一次慢速的内存访问。
  • 采用 Write-Through Write-Allocate:

    1. Write-Allocate 触发: CPU 在缓存中分配一个缓存行,对应地址范围 0x1000 - 0x103F。
    2. 缓存行填充: CPU 发起一个突发读(Burst Read)操作,将整个 0x1000 - 0x103F 的数据块从内存读入刚分配的缓存行。
    3. 缓存更新: CPU 在缓存中更新 0x1000 处的 4 个字节为新数据。
    4. Write-Through 触发: 由于是写命中且策略是 Write-Through,CPU 将这 4 个字节的新数据写入主内存。
    5. 操作结束。缓存中 now 拥有了该内存区域的副本。
    • 结果: 写入完成,一致性得到保证。此外,整个 0x1000 - 0x103F 区域的数据都被加载到了缓存中。如果 CPU 马上要读 0x1004,会发生读命中,数据直接从高速缓存中返回。

这个组合的最终效果是:

  • 牺牲了单次写入的延迟: 一次原本简单的直接写内存操作,变成了“读整个缓存行 + 写缓存 + 写内存”的更复杂操作,延迟更高。
  • 换取了后续访问的超高速度: 为后续对该内存区域的一系列读写操作铺平了道路,它们都能以缓存速度进行。
  • 始终保持一致性: 因为每一步都遵循 Write-Through 原则。

为什么需要它?—— 应用场景

这种策略适用于对一片新的、未缓存的内存区域进行初始化或密集的写后读操作。

  1. 堆内存的初始化:

    • 场景: 程序调用 malloc() 分配了一块新内存,然后立即用 memset() 或一个循环对其进行清零或初始化。这块内存之前未被访问过,肯定不在缓存中。
    • 为什么用它:
      • 第一次写入 0x1000(Write-Through Write-Allocate)虽然慢,但它将整个缓存行载入了缓存。
      • 接下来初始化 0x1004, 0x1008, ... 0x103F 的写入操作,全部变成缓存命中的 Write-Through 写入,速度极快。
      • 如果没有 Write-Allocate,每一次对未缓存地址的写入都要直接访问内存,速度会慢得多。
  2. 创建需要共享的数据结构:

    • 场景: CPU 核心需要初始化一个数据结构(例如一个任务描述符),然后立即通知另一个核心或 DMA 控制器去读取它。
    • 为什么用它:
      • Write-Through: 保证了另一个设备读取时,能从内存中看到完全初始化好的数据。
      • Write-Allocate: 在 CPU 核心初始化该结构时,由于缓存行的加载,后续的字段写入都在缓存中完成,加快了初始化速度。CPU 核心自身如果很快要读取该结构,也能从缓存中快速访问。
  3. 对非时间顺序访问的写入:

    • 场景: 写入一个数组,但索引是跳跃的、非顺序的。
    • 为什么用它: Write-Allocate 加载的是整个缓存行。即使你只写一个字节,它也会把附件的几十个字节都拉进来。当你接下来跳跃到写入同一个缓存行内的另一个地址时,这次写入就是缓存命中的,速度非常快。

在 AXI 协议中的表示

在 AXI 协议中,这是通过 AWCACHE 信号实现的(因为 Write-Allocate 主要与写事务相关)。

  • AWCACHE[1] (Cacheable): 1 (表示是 Cacheable 的)
  • AWCACHE[3] (Write-Allocate): 1 (表示 Enable Write-Allocate)
  • AWCACHE[2] (Read-Allocate): 通常为 0 (但也可以为1)
  • AWCACHE[0] (Bufferable): 通常为 0 (强调写入必须到达内存)

一个典型的 Write-Through Write-Allocate 配置是 AWCACHE = 4'b1110。

  • C=1: Cacheable
  • WA=1: Write-Allocate
  • RA=0: No Read-Allocate (也可以是1,但WA=1是核心)
  • B=0: Not Bufferable

对比总结

属性 写未命中行为 优点 缺点 适用场景
Write-Through Write-Allocate 分配缓存行,读入数据,更新缓存,写透内存 加速后续访问,强一致性 首次写入延迟极高 初始化一块后续要频繁访问的共享内存
Write-Through No-Allocate 不分配,直接写内存 避免缓存污染,强一致性,首次写入快 后续访问可能慢 写入后不再读取的共享数据(如帧缓冲区)
Write-Back Write-Allocate 分配缓存行,读入数据,只更新缓存 写入性能极高,加速后续访问 数据非强一致,风险高 处理器私有数据的初始化
Non-cacheable 直接写内存 强一致,简单 所有访问都慢 设备寄存器

总结

Write-Through Write-Allocate 是一种 “用一次性的性能代价换取长期收益” 的缓存策略。

  • 它的设计哲学: “磨刀不误砍柴工”。宁愿在第一次写入时花费额外开销(“磨刀”——分配并加载缓存行),也要为后续大量的访问操作做好极速响应的准备(“砍柴”——缓存命中)。
  • 它的核心价值: 它是数据初始化阶段的优化利器,特别适用于那些初始化后立即被本CPU核心或其他设备频繁访问的共享数据区域。
  • 它的代价: 单次写入延迟高,并且占用总线带宽(因为需要先执行一个缓存行填充的读操作)。

简单来说,如果你知道一块内存马上就要被“热”起来,使用这个属性是值得的。如果你知道它只会被“冷”写一次,那么就应该避免使用它。

posted on 2025-09-15 22:40  SOC验证工程师  阅读(13)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3