0%

Champsim Cache

Champsim Cache

坐标:: cache.h , cache.cc

class::CACHE

构造

构造函数:
CACHE::CACHE(champsim::cache_builder<>) ,由全局 env 通过 cache_builder 初始化

cache_builder

模板类,需要的typename P 和 R 均继承自 cache_builder_module_type_holder。其中,P 代表 prefetcher,R 代表 replacement,通过模板的偏特化函数选择相应的类。

成员

upper_levels/lower_level:
上/下级 cache 接口,channel_type(接口协议类)。对于 L1 Cache, upper_levers 连接 core 。

lower_translate:
地址转换接口,channel_type。对于 L1Cache 而言,该端口连接 L1TLB;

问题: 为什么 L2Cache 的该端口连接的是 L2 TLB?# ; LLC 的该端口留空。

MAX_TAG:
每个周期最多可以同时接受几个 tag 访问请求,对于多端口 cache 而言,MAX_TAG > 1

MAX_FILL:
每个周期最多可以同时接受几个 refill 响应,取决于总线协议

inflight_tag_check:
在飞的tag比较请求队列。每个周期开始时,会先从 translation_stash 中取出完成转换的请求,然后从上级 cache 中取出可以进行地址转换的请求,但是每个周期能取到的请求总数限制于 initiate_tag_bw 带宽之下。

translation_stash:
地址转换请求的暂存队列

pref_module_pimpl / repl_module_pimpl:
预取器、替换策略的物理实现

成员类

  • 请求类
    • tag 查找请求 : tag_lookup_type
    • mshr 请求 : mshr_type
  • 子系统类
    • 预取器 prefetcher_module_concept
    • 替换策略 replacement_module_concept
  • mshr_type
    用于访问和处理

data_promise:
waitable 对象,下级 cache 返回数据后被赋值。当赋值完成的 cycle 到来时会被调入 handle_fill 处理

operable 接口实现

  • initialize()
    • 预取器初始化: impl_prefetcher_initialize() ,直接调用成员预取器的 impl_prefetcher_initialize()
    • 替换策略初始化: impl_initialize_replacement() ,直接调用成员替换策略的 impl_initialize_replacement()
  • begin_phase(): 初始化各统计变量
  • end_phase(unsigned cpu): 收集并统计最终的各统计变量的结果
  • operate()
    • 调用所有上级cache 的 check_collision()
    • Finish returns : 处理下级 cache 的响应
      • 对于下级 cache returned 队列中的每个响应调用 finish_packet
      • 计算下级 cache returned 队列中的响应个数累加到 progress
      • 清空下级 cache 的 returned 响应队列
    • Finish translations : 如果 cache 存在TLB端口, 处理 TLB 返回的结果
      • 处理过程同下级 cache
    • Perform fills :对于 MSHR 队列和 inflight_writes 队列
      • 在 fill_bw 带宽内,每个队列选择所有 data_promise 已准备好的 entry(应当在队列的头部保存所有准备好 fill 的 entry)
      • 对 fill 范围的每个请求进行 handle_fill, 遇到第一个 handle 失败的作为结束地址
      • 将 handle 成功的请求移出该队列,并消耗相应 fill_bw 带宽
    • 计算本周期可以接受的 tag 请求带宽,并将 translation_stash 中完成转换的请求加入 inflight_tag_check 队列
      • 计算出当前周期可以接受的新的 tag_check 请求的带宽 : initiate_tag_bw
      • 在 initiate_tag_bw 的带宽下,对于 translation_stash 中的请求,如果完成地址转换,将其移入 inflight_tag_check,并消耗相应带宽
      • [red]#如果有多个上级 cache,交换这些cache的位置?(看不懂)#
    • 将上级 cache 中的 tag 请求,在带宽约束下移入 inflight_tag_check 队列
      • 对于上级 cache 的所有请求队列,计算出每个上级cache可以占用的tag带宽
      • 在每个上级cache能占用的带宽之下,将请求队列中可以进行地址转换的的请求移入 inflight_tag_check,并消耗相应的 initiate_tag_bw
    • 发射地址转换请求 : 对每个请求依次检查是否完成地址转换,若未完成,则通知TLB: issue_translation()
      • 对于 inflight tag check 中的请求而言,是第一次发送地址转换请求
      • 对于 translation_stash 中的请求而言,是上一个周期未完成的地址转换请求
    • 本周期 inflight_tag_check 中未完成的地址转换请求加入 translation_stash
    • Perform tag checks
      • 根据目前 inflight tag check 请求找到其中已经完成地址转换的请求得到请求地址范围
      • 将这些tag请求依次 try_hit
      • 对于 tag 比较结果为 miss 的请求,进行 do_handle_miss
      • 消耗相应的 tag_check 带宽 (每次 operate 最大带宽为 MAX_TAG )
      • 从 inflight tag check 请求中删除已完成的 tag check 请求
    • 调用预取器的 operate()
    • 返回消耗的总带宽:包括 下级返回数据和完成地址转换消耗的带宽 + fill消耗带宽 + 初始化tag带宽 + tag check带宽


do_handle_miss():

  • 对于没有设置 m_wq_full_addr 的 write miss , 调用 handle_write 处理请求:此时的请求属于写回miss后的替换block
  • 对于其他请求,调用 handle_miss

至于 m_wq_full_addr 到底什么意思不太懂?champsim default 设置中 l1cache 均设置了该变量,而 l2 和 llc 均未设置该变量

带宽消耗:
最终 operate() 返回所有操作的请求数:

progress:
包括:
. 下级 cache 每周期响应返回请求数据的个数
. TLB 每周期响应返回请求数据的个数
. 每周期从 inflight_tag_check 移入 translation_stash 的请求数

initiate_tag_bw:
每个周期可以接受的新的 tag 访问请求,用于补充 inflight_tag_check 队列。主要消耗来自:

  1. 每个周期刚开始 translation_stash 中已完成地址转换的请求
  2. 上级 cache 中可以进行地址转换的请求

fill_bw:
每周期可以处理的 refill 数量,用于处理 MSHR 队列和 inflight_writes 中的未响应请求

tag_check_bw:
每周期可以访问的 tag 的请求数

CACHE 操作实现

  • finish_packet() : 处理下级 cache 返回的数据

    1. 找到 MSHR 队列中地址匹配的 entry , 并将下级 cache 返回的数据和将来完成 fill 的时间打包为 waitable 对象并赋值给其 data_promise
    2. 找到 MSHR 队列中第一个 data_promise 未赋值(意味着下级 cache 尚未返回该 entry 请求的数据) 的 entry,将地址匹配的 entry 和该entry交换位置,目的是希望所有 data_promise 已完成的 entry 都放到队列的前面以便于处理 refill。
  • finish_translation()

    • 找到 translation_stash 中未完成地址转换的 entry,从其中找出与 TLB 返回的地址匹配的 entry
    • 将这些 entry 标记为完成地址转换并将转换后的物理地址写入相应的entry: mark_translated
    • 将 inflight_tag_check 队列中的所有地址匹配的 entry 都 mark_translated
  • handle_fill() : 根据下级cache返回的数据 refill 当前cache

    1. 根据地址确定要 refill 的 cache set
    2. 如果该 set 的各个 way 均 valid ,则调用 impl_find_victim 找到要替换的 way ;如果有 way 不是 valid ,优先选该 way 做替换
    3. 找到要替换的 way 后,如果该 way valid 且 dirty,需写回下级 cache , lower_level->add_wq(writeback_packet)
    4. 通知 prefetcher 和 replacement 此次 refill 的具体信息,便于其更新状态
      • impl_prefetch_cache_fill , impl_replacement_cache_fill
    5. 完成真正的 refill 操作: fill_block 直接赋值给 cache block
    6. 将 refill 的具体信息打包为 mshr 的 response_type 加入 该 mshr entry 的 to_return 队列
      (此时原先 miss 的请求再次 <> 将 hit 并返回数据同时移出 MSHR 队列)

impl_find_victim, impl_replacement_cache_fill 均由 replament 模块的成员 repl_module_pimpl 实现

  • try_hit() : 比较 tag ,检查是否 hit
    1. 根据地址确定要访问 的 cache set, 没找到则 miss
    2. 更新 prefetcher 和 replacement:
      impl_prefetcher_cache_operate , impl_update_replacement_state
    3. 如果 hit
    • 将响应加入请求 handle_pkt 的 to_return 队列
    • 对于写请求,置位 hit block 的 dirty 位
    • 如果该 hit block 是预取来的,将 prefetch 位置0

Champsim trace 信息并不包含访问的字节数, cpu 对内存的访问是否 hit 只会通过比较 block_num 和 block_offset 决定,并不会实际返回数据。

  • handle_miss()

    1. 将请求包装成 mshr 请求
    2. 查找 MSHR 队列中是否有地址匹配的 entry
    • 如果有,则将当前请求和该 entry merge 到一起
    • 如果 MSHR 队列已满,则 处理失败,返回 false
    • 否则(无匹配项且队列未满):
      • 向下级 cache 发送读取请求 lowerlevel->add_rq()
      • 为该请求分配一个 MSHR entry
  • handle_write()

    1. 将请求打包为 mshr_type , 设置 FILL_LATENCY 后触发事件
    2. 将该 mshr_type 加入 inflight_writes