Home > technology > arm64 TLB 硬件设计

arm64 TLB 硬件设计

TLB flush 硬件行为

arm64 与 x86 在 tlb flush 差异挺大,比如 flush 的范围、同步机制。x86 的 tlb flush 一般是(1)只处理当前核,其他核是否需要刷有操作系统内核去判断,这算是同步机制的范畴,依赖软件发起 IPI 到需要的核,(2)而 arm64 的 TLBI 的范围是整个共享域,以不成熟的经验,arm64 一般只会设计一个共享域,是否其他核也需要同步,是由硬件上去判断。

先从最简单的场景,TLBI 只刷一个核开始。

首先,TLBI 在硬件上并不是一个高优的指令,在执行 TLBI 时,首先会将其存到具有 4 个条目的后台任务队列 STQ 中,等 MMU 主流水线有空闲周期时,开始处理 STQ 队列中的任务。下面是其中一个条目,命名分别是 stq0-stq3:

always_ff @(posedge clk)
  begin: u_mm_stq0_type_q_1_0_grp
    if (mm_stq0_en == 1'b1) begin
      mm_stq0_type_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_type[1:0];
      mm_stq0_va_q[`PERSEUS_MMU_VA] <= `PERSEUS_DFF_DELAY l2_cpu_snp_addr[`PERSEUS_MMU_SNP_VA_RANGE];
      mm_stq0_va_valid_q <= `PERSEUS_DFF_DELAY l2_cpu_snp_va_valid_ql;
      mm_stq0_vmid_valid_q <= `PERSEUS_DFF_DELAY l2_cpu_snp_vmid_valid;
      mm_stq0_vmid_q[`PERSEUS_MMU_VMID_RANGE] <= `PERSEUS_DFF_DELAY l2_cpu_snp_vmid[`PERSEUS_MMU_VMID_RANGE];
      mm_stq0_asid_valid_q <= `PERSEUS_DFF_DELAY l2_cpu_snp_asid_valid;
      mm_stq0_asid_q[`PERSEUS_MMU_ASID_RANGE] <= `PERSEUS_DFF_DELAY l2_cpu_snp_asid[`PERSEUS_MMU_ASID_RANGE];
      mm_stq0_sec_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_sec[1:0];
      mm_stq0_el_q[2:0] <= `PERSEUS_DFF_DELAY mm_stq_el_nxt[2:0];
      mm_stq0_leaf_q <= `PERSEUS_DFF_DELAY l2_cpu_snp_leaf;
      mm_stq0_stage_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_stage[1:0];
      mm_stq0_ttl_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_ttl[1:0];
      mm_stq0_tg_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_tg[1:0];
      mm_stq0_num_q[4:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_num[4:0];
      mm_stq0_scale_q[1:0] <= `PERSEUS_DFF_DELAY l2_cpu_snp_scale[1:0];
      mm_stq0_range_q <= `PERSEUS_DFF_DELAY l2_cpu_snp_range_ql;
    end

当开始处理 STQ 中的任务时,会从条目中选择一个最老的指令,并判断其类型,如果满足:mm_stq_type[1:0] == 2'b00,说明是一个 TLBI 任务,mm_stq_tmo 信号会拉高,同时mm_tc_non_crit_snp_tmo_nxt 也会拉高。

assign mm_stq_tmo = (mm_stq_type[1:0] == 2'b00);
assign mm_tc_non_crit_snp_tmo_nxt = mm_stq_req_vld & mm_stq_tmo;

mm_tc_non_crit_snp_tmo_nxt 信号会被发送到 TLB Array。


接下里,会在 TLB Array 进行寻找匹配的条目。

发送到 TLB Array 的信号中可以提取到 asid、va 等信息,如果匹配到条目,mm_tc_hit_for_rams_t3 信号会被拉高。


同时,匹配信号和 tmo 探测信号会合成mm_tc_tmo_write_for_rams_t3, 并通过条件向量匹配,生成一个命中 Way 的写使能信号mm_tc_chip_select_t1。

assign mm_tc_tmo_write_for_rams_t3 = mm_tc_hit_for_rams_t3 &
  mm_tc_snp_tmo_t3_q & mm_tc_vld_t3_q & ~mm_tc_slow_tmo_inv_t3_q;

  always_comb 
  begin: u_mm_tc_chip_select_t1_4_0
    casez({mm_tc_non_crit_req_t1, mm_tc_parity_err_t3, mm_tc_parity_err_t4, mm_tc_alloc_write_ql_t3, mm_tc_tmo_write_for_rams_t3, mm_tc_slow_tmo_write_t4})
      6'b000000: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = {`PERSEUS_MMU_TC_WAYS{(mm_tc_req_vld_t1&(~(mm_tc_req_mmu_dis)))}};  
      6'b?????1: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = (mm_tc_way_hit_for_snp_t4_q[`PERSEUS_MMU_TC_WAY_MSB:0]|({`PERSEUS_MMU_TC_WAYS{mm_tc_parity_err_t4}}&mm_tc_way_parity_err_for_rams_t4_q[`PERSEUS_MMU_TC_WAY_MSB:0]));  
      6'b????10: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = (mm_tc_way_hit_raw_for_rams_t3_q[`PERSEUS_MMU_TC_WAY_MSB:0]|({`PERSEUS_MMU_TC_WAYS{mm_tc_parity_err_t3}}&mm_tc_way_parity_err_for_rams_t3_q[`PERSEUS_MMU_TC_WAY_MSB:0]));  
      6'b???100: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = mm_tc_rrip_chip_select_t3[`PERSEUS_MMU_TC_WAY_MSB:0];  
      6'b??1000: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = mm_tc_way_parity_err_for_rams_t4_q[`PERSEUS_MMU_TC_WAY_MSB:0];  
      6'b?10000: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = mm_tc_way_parity_err_for_rams_t3_q[`PERSEUS_MMU_TC_WAY_MSB:0];  
      6'b100000: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = ((mm_tc_non_crit_chip_select_q[`PERSEUS_MMU_TC_WAY_MSB:0]&{`PERSEUS_MMU_TC_WAYS{((~(mm_mbist_en_q))|mm_mbist_tc_acc_t1)}})|{`PERSEUS_MMU_TC_WAYS{(mm_mbist_en_q&mm_mbist_all_mode_t1)}});  
      default: mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = {5{1'bx}};
    endcase
`ifdef PERSEUS_XPROP_CASE
    if((^({mm_tc_non_crit_req_t1, mm_tc_parity_err_t3, mm_tc_parity_err_t4, mm_tc_alloc_write_ql_t3, mm_tc_tmo_write_for_rams_t3, mm_tc_slow_tmo_write_t4})) === 1'bx)
      mm_tc_chip_select_t1[`PERSEUS_MMU_TC_WAY_MSB:0] = {5{1'bx}};
`endif
  end

mm_tc_slow_tmo_write_t4 也是一种 TMO 信号,但与 TLBI 不同,slow tmo 的源头是内部硬件页表漫游状态机。在 MMU 缺页遍历过程中,可能会发现引起 TLB 别名冲突的情况(比如 contiguous 分裂页)。这种情况,硬件会触发一个 slow tmo 请求,强制将可能重叠的老旧 tlb entry 失效。

tmo_write 在条件向量里边在倒数第二个,属于“6'b????10”,写信号只会落在命中的哪一路(Way)。


有了“写使能信号”和“被选中的那一条 Way”之后,最后一个问题是:写入的数据是什么内容?

mm_tc_data_in_t1 是送到 TLB Array 的原始数据。在下面的多路选择器中,TMO 失效写属于后者,原始数据将会被全部设置为 0({`PERSEUS_MMU_TC_WIDTH{1'b0}})。到此,TLBI 失效指令的擦除任务就完成了。

assign mm_tc_data_in_t1[`PERSEUS_MMU_TC_DATA_RANGE] =
  (mm_tc_alloc_write_ql_t3 | mm_mbist_en_q) ?
  mm_tc_non_crit_wr_data_q[`PERSEUS_MMU_TC_DATA_RANGE] : {`PERSEUS_MMU_TC_WIDTH{1'b0}};

后续补上对于多核,tlbi snoop 的大致流程。

2M/4K TLB entries 处理

arm 与 x86 在 TLB 设计上有一个明显的差异,x86 的 tlb entries 是区分 4K、2M 和 1G 的,一个 tlb entry 不能既能缓存 4k 页表,还能缓存 2M 页表,而 arm 的 tlb entry 是不区分页面大小。arm 在设计上花了更多的逻辑处理页面大小,比如在 tlb entry 上划分有 PGSZ 字段保存当前页面大小,同时也支持 tlb 合并功能,连续的 4k、16k 等可以合并成一个支持更大页面的 tlb entry,用于节省 tlb 资源。


从mm_tsm_s1_s2_pgsz 信号开始:

`define PERSEUS_MMU_TC_DATA_COLT                   6
`define PERSEUS_MMU_TC_DATA_RSLVD_PGSZ             `PERSEUS_MMU_TC_DATA_COLT+13:`PERSEUS_MMU_TC_DATA_COLT+11
`define PERSEUS_MMU_TC_DATA_RSLVD_PGSZ_M1          `PERSEUS_MMU_TC_DATA_COLT+12:`PERSEUS_MMU_TC_DATA_COLT+11

assign mm_tsm_s1_s2_pgsz[6:0] = mm_tsm_s1_final_pgsz_dec[6:0] | mm_tsm_s2_desc_pgsz_dec[6:0];

mm_tsm_s1_s2_pgsz[6:0] 的每一位对应一级基础翻译块的大小,如下:

  • [0]: 表示这是一个基础粒度为 4KB 的 Page。
  • [1]: 表示这是一个基础粒度为 16KB 的 Page。
  • [2]: 表示这是一个基础粒度为 64KB 的 Page。
  • [3]: 表示这是一个基础粒度为 2MB 的 Block(通常来自 4KB Granule 的 Level 2 Block 或 16KB 连续块相关状态)。
  • [4]: 表示这是一个基础粒度为 32MB 的 Block(通常来自 16KB Granule 的 Level 2 Block)。
  • [5]: 表示这是一个基础粒度为 512MB 的 Block(通常来自 64KB Granule 的 Level 2 Block)。
  • [6]: 表示这是一个基础粒度为 1GB 的 Block(通常来自 4KB Granule 的 Level 1 Block)。


下面是一段场景的优先级编码器,主要找出mm_tsm_s1_s2_pgsz[6:0] 中第一个出现 1 的位置:

assign mm_tsm_s1_s2_rslvd_pgsz[2] =
    (mm_tsm_s1_s2_pgsz[6]&!mm_tsm_s1_s2_pgsz[5] &!mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[5]&!mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0]);

  assign mm_tsm_s1_s2_rslvd_pgsz[1] =
    (mm_tsm_s1_s2_pgsz[6]&!mm_tsm_s1_s2_pgsz[5]&!mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[5]&!mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_rslvd_colt&mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0]);

  assign mm_tsm_s1_s2_rslvd_pgsz[0] =
    (mm_tsm_s1_s2_pgsz[6]&!mm_tsm_s1_s2_pgsz[5]&!mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_pgsz[4]&!mm_tsm_s1_s2_pgsz[3]&!mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_rslvd_colt&mm_tsm_s1_s2_pgsz[2]&!mm_tsm_s1_s2_pgsz[1])
    | (!mm_tsm_s1_s2_rslvd_colt&mm_tsm_s1_s2_pgsz[1]&!mm_tsm_s1_s2_pgsz[0])
    | (mm_tsm_s1_s2_rslvd_colt&mm_tsm_s1_s2_pgsz[0]);

以mm_tsm_s1_s2_rslvd_pgsz[2] 为例,其为 1 有四种情况:

  1. mm_tsm_s1_s2_pgsz[6] 为 1,且其余 LSB 0-5 都为 0;
  2. mm_tsm_s1_s2_pgsz[5] 为 1,且其余 LSB 0-4 都为 0;
  3. mm_tsm_s1_s2_pgsz[4] 同上类推;
  4. mm_tsm_s1_s2_pgsz[3] 同上类推;


mm_tsm_s1_s2_rslvd_pgsz[0] 和mm_tsm_s1_s2_rslvd_pgsz[1] 信号逻辑类似,多出了一个mm_tsm_s1_s2_rslvd_colt 合并信号也能合成对应的 PGSZ。关于 TLB 合并的细节就不在此处介绍。

RSLVD_PGSZ[2:0]

对应的有效命中页大小

典型应用场景 / 来源溯源

3'b000

(0)

4KB

纯粹的 4KB 小页,无 Colt(硬件聚合)。

3'b001

(1)

16KB

  • 纯 16KB 粒度的正常小页;
  • 四个 4KB 依靠 Colt 底层连片聚合成的。

3'b010

(2)

64KB

  • 纯 64KB 粒度的正常小页;
  • 四个 16KB 依靠 Colt 底层连片聚合成的。

3'b011

(3)

256KB

四个 64KB 依靠 Colt 底层连片聚合而成的(ARM 架构中本身没有独立的 256K 页面结构)。

3'b100

(4)

2MB

4KB 粒度对应的 2MB Block 大页。

3'b101

(5)

32MB

16KB 粒度对应的 32MB Block 大页。

3'b110

(6)

512MB

64KB 粒度对应的 512MB Block 大页。

3'b111

(7)

1GB (或以上)

4KB 粒度对应的超级大页 1GB(部分连带逻辑可能复用这档处理最大的比如 64GB 映射)。


最终,mm_tsm_s1_s2_rslvd_pgsz[2:0] 会被写入到 tlb:

assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_RSLVD_PGSZ] = mm_tsm_s2_in[5] ? mm_tsm_s2_err_info[17:15] : mm_tsm_s1_s2_rslvd_pgsz[2:0];

assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_S1_PGSZ]    = mm_tsm_tc_entry_s1_pgsz[2:0];
assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_S1_PXN]     = mm_tsm_tc_entry_s1_pxn & mm_tsm_s1_mmu_en;
assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_S1_XN]      = mm_tsm_tc_entry_s1_xn  & mm_tsm_s1_mmu_en;
assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_S2_PXN]     = mm_tsm_s2_in[5] ? 1'b0 : mm_tsm_s2_desc_pxn;
assign mm_tsm_s1_s2_rslvd_attr[`PERSEUS_MMU_TC_DATA_S2_XN]      = mm_tsm_s2_in[5] ? 1'b0 : mm_tsm_s2_desc_uxn;

可以发现,这里有两个 PGSZ:PERSEUS_MMU_TC_DATA_RSLVD_PGSZ、PERSEUS_MMU_TC_DATA_S1_PGSZ,其中(1)RSLVD_PGSZ 表示实际硬件上存储的真实有效的页面大小,例如虚拟化场景,需要考虑 S1 和 S2,取最小识别的尺寸,同时也会存在TLB Coalescing 合并的情况;(2)S1_PGSZ 主要是指 S1 页表识别的大小,比如 Guest 在 S1 阶段解析的页面大小。


综上所述,其实就是 TLB 页表项中有 PGSZ 字段用于存储当前页表项所表示的页面大小。


页表 AF 更新逻辑

根据 ARMv9 的设计,在 TLB miss 后读取目标的页表,获取内存中的页表数据后,如果发现 AF 为 0,则会在总线上触发一个 CAS 事务操作修改内存中 AF 位。同时,也会将写入 TLB 项的 AF 置为 1。这是操作系统判断页面冷热的基础机制。


在下面的逻辑信号中,~mm_tsm_desc_af 表示如果 AF 为 0,则会拉高mm_hw_af_upd_s1_pq。

assign mm_hw_af_upd_s1_pq = ~mm_tsm_desc_af        & // 发现当前描述符AF=0
                            mm_tsm_s1_desc_done_q  & // 描述符读取完成
                            ~mm_tsm_s1_wlk_pending &
                            mm_tcr_elx_ha_q;         // TCR_EL1.HA 位已开启(硬件管理使能)

assign mm_hw_ad_upd_s1 = (mm_hw_af_upd_s1 | mm_hw_dbm_upd_s1) &
                         ~mm_tsm_rd_type_q &
                         ~mm_tsm_desc_sbe_q;

mm_hw_af_upd_s1_pq 属于 s1 硬件 AF 更新使能信号,后续,会将 AF 和 DBM(dirty 位更新)合并为mm_hw_ad_upd_s1 信号,该信号会在总线上触发一个 CAS 事务修改内存页表。


同时,MMU 模块也会根据以上信号判断在填充到 TLB 前是否将 AF 根据为 1:

assign mm_tsm_desc_af_nxt = mm_tsm_s1_desc_fth_req ?
                            (mm_tsm_desc_data_ee[10] | mm_hw_af_upd_s1_pq) :
                            (mm_tsm_desc_data_ee[10] | mm_hw_af_upd_s2_pq) ;

内存上页表 AF 更新和填充到 TLB 上的 AF 更新没有前后约束,理论上 TLB 上的 AF 更新快一步。

当前内存上的 AF 被重新设置为 0 时,硬件上会有什么反应?

若操作系统清除某个页表项的内存中的 AF 位,且没有执行 TLB flush,硬件 TLB 是不会感知,即硬件上该项页表的 AF 还是为 1,只有等该页表项失效后,例如被替换或主动 flush,才会重新读取内存中页表数据。因此,操作系统在更新内存中的 AF 位后,总是会执行一次 tlb flush,目的就是失效对应的 TLB entry,下次重新检查 AF 才能表示这段时间是否被访问。

Categories: technology Tags: , ,
  1. No comments yet.