TLB — Translation Lookaside Buffer

地址翻译缓存:命中/未命中与替换策略

📖 为什么需要TLB?

SV39三级页表翻译需要3次内存访问,加上最终的数据访问共4次。TLB缓存最近的翻译结果,消除重复查找:

无TLB: 每次地址翻译需要4次内存访问 ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ L1 │→│ L2 │→│ L3 │→│ DATA │ │ PTE │ │ PTE │ │ PTE │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ 访存1 访存2 访存3 访存4 有TLB: 命中时只需1次数据访问 ┌──────┐ ┌──────┐ │ TLB │→│ DATA │ │ HIT! │ │ │ └──────┘ └──────┘ 0次额外访存 1次访存 TLB命中率通常>99% • 局部性原理: 程序短时间内访问的页面有限 • 4KB页面 + 64项TLB = 256KB覆盖范围
参数典型值说明
TLB项数32-128更多项=更高命中率
页面大小4KB标准页, 可用大页(2MB/1GB)
命中率>99%程序局部性保证
缺失代价3次访存+1次数据SV39三级页表
替换策略LRU/NRU决定淘汰哪个项

🔬 TLB命中/未命中处理

TLB工作流程: CPU发出虚拟地址 │ ▼ ┌─────────┐ HIT ┌──────────┐ │ TLB查找 │───────→│ 物理地址 │──→ Cache/内存 └────┬────┘ └──────────┘ │ MISS ▼ ┌─────────┐ ┌──────────┐ │ 页表遍历│───────→│ 填充TLB │──→ 重试访问 │ (3级) │ │ (LRU替换)│ └─────────┘ └──────────┘ TLB刷新场景: • 上下文切换: 切换进程时需刷新(satp写入) • 页表修改: 修改PTE权限后需刷新(SFENCE.VMA) • 延迟刷新: 使用ASID避免全刷新
RISC-V使用SFENCE.VMA指令刷新TLB。与x86的INVLPG和ARM的TLBI类似,但SFENCE可以指定虚拟地址范围,减少不必要的刷新。

🖥️ Verilog实现:4项TLB

// Lesson 10: TLB — Translation Lookaside Buffer (4-entry, LRU)
module tlb(
    input  wire        clk, rst_n,
    input  wire        lookup_req,
    input  wire [38:0] vaddr,
    output reg  [55:0] paddr,
    output reg         hit, valid,
    input  wire        fill_req,
    input  wire [38:0] fill_vaddr,
    input  wire [55:0] fill_paddr,
    input  wire [1:0]  fill_perm,
    input  wire        fill_valid
);
    parameter ENTRIES = 4;
    reg [38:0] tag [0:ENTRIES-1]; reg [43:0] ppn [0:ENTRIES-1];
    reg [1:0] perm [0:ENTRIES-1]; reg entry_valid [0:ENTRIES-1];
    reg [1:0] lru_cnt [0:ENTRIES-1];
    integer i;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for(i=0;i<ENTRIES;i=i+1) begin entry_valid[i]<=0; lru_cnt[i]<=0; end
        end else begin
            if (fill_req) begin : find_lru
                reg [1:0] rep_idx; rep_idx=0;
                for(i=1;i<ENTRIES;i=i+1)
                    if(lru_cnt[i]<lru_cnt[rep_idx]) rep_idx=i[1:0];
                tag[rep_idx]<=fill_vaddr; ppn[rep_idx]<=fill_paddr[55:12];
                perm[rep_idx]<=fill_perm; entry_valid[rep_idx]<=fill_valid;
                lru_cnt[rep_idx]<=3;
            end
            for(i=0;i<ENTRIES;i=i+1)
                if(lru_cnt[i]>0) lru_cnt[i]<=lru_cnt[i]-1;
        end
    end
    always @(*) begin
        hit=0; paddr=0; valid=0;
        for(i=0;i<ENTRIES;i=i+1)
            if(entry_valid[i] && tag[i]===vaddr)
                begin hit=1; paddr={ppn[i],vaddr[11:0]}; valid=1; end
    end
endmodule
Verilator仿真验证通过 — TLB命中/未命中正确

代码解析

🧪 实验练习

  1. 扩展到32项TLB:使用CAM或SRAM实现
  2. 添加ASID支持:避免上下文切换时全刷新
  3. 实现大页TLB:支持2MB/1GB超级页映射
  4. 添加TLB预取:预测即将访问的页面,提前填充
TLB命中/未命中正确
思考题:如果一个程序顺序访问1MB内存(256个4KB页面),而TLB只有64项,会发生什么?如何优化?
参考资料:RISC-V Privileged Spec §4.3.1 | TLB设计原理 | ASID与TLB管理

🔗 TLB在处理器中的位置

TLB位于CPU和Cache之间,是地址翻译的第一站:

现代RISC-V处理器存储层次: CPU核心 │ 虚拟地址 ▼ ┌──────┐ │ ITLB │ ← 指令TLB (通常全相联, 32-64项) └──┬───┘ │ 物理地址 ▼ ┌──────┐ │ DTLB │ ← 数据TLB (通常全相联, 32-64项) └──┬───┘ │ 物理地址 ▼ ┌──────┐ ┌──────┐ │ L1-I │ │ L1-D │ ← 一级Cache (4-8路, 32-64KB) └──┬───┘ └──┬───┘ │ │ └─────┬──────┘ ▼ ┌──────┐ │ L2 │ ← 二级Cache (8-16路, 256KB-4MB) └──┬───┘ ▼ ┌──────┐ │ L3 │ ← 三级Cache (共享, 8-32MB) └──┬───┘ ▼ 内存 STLB (共享TLB): L2级TLB, 512-2048项 作为ITLB/DTLB miss后的后备

TLB一致性

当操作系统修改页表(如换页、修改权限)时,必须保证TLB与页表一致:

🎯 本课与整体课程的关系

TLB知识图谱: 09 SV39 → 10 TLB (本课) ↓ TLB加速虚拟地址翻译 ↓ 11 直接映射Cache — 翻译后访问 12 组相联Cache — 更高效的缓存

📚 延伸阅读与参考资料

资料内容链接
RISC-V特权规范CSR、Trap、中断完整定义riscv.org/specifications
RISC-V手册中文版免费教材crva.ict.ac.cn
OpenSBI源码M-mode固件参考实现github.com/riscv/opensbi
Linux RISC-V内核移植与驱动kernel.org
BOOM处理器UC Berkeley开源OoO核心github.com/riscv-boom/riscv-boom
香山处理器中科院开源高性能核心github.com/OpenXiangShan

相关课程

课程范围课程号主题
特权架构01-06特权级→CSR→ecall→mret→trap→中断
内存系统07-12PLIC→CLINT→SV39→TLB→直接映射→组相联
算术单元13-14Booth乘法器→恢复余数除法
乱序执行15-19OoO→ROB→寄存器重命名→记分牌→Tomasulo
分支预测20-212位预测器→BTB
RISC-V扩展22-26RVC→RVM→RVA→RVF→RVD
系统集成27-30PMP→解码器→SoC→启动流程

实验环境搭建

建议使用以下环境进行实验:

📊 TLB性能测量与优化

TLB性能可以通过以下方式测量和优化:

// TLB缺失率测量 (RISC-V): csrr t0, minstret // 读取指令数 // 执行测试代码 csrr t1, minstret // 通过性能计数器读取TLB缺失次数 // (如果实现了mhpmcounter事件) TLB缺失率 = TLB_misses / total_accesses 典型值: 顺序访问: <0.1% (极好) 随机4KB页: ~1-5% (可接受) 随机4KB页(工作集>TLB): >50% (需要大页)

软件TLB管理策略

🔬 TLB的硬件实现选择

TLB的硬件实现有多种选择,影响面积、功耗和速度:

实现方式查找方式面积功耗速度
全相联CAM并行比较所有项最快(1周期)
组相联SRAMIndex索引+Tag比较快(1-2周期)
软件TLB软件遍历页表最小最低慢(10+周期)

现代处理器通常L1 TLB使用全相联CAM(32-64项),L2 STLB使用组相联SRAM(512-2048项)。CAM的功耗与项数成正比,所以L1 TLB不能太大。

TLB与Cache的地址翻译流水线

虚拟地址到Cache访问的流水线: Cycle 1: TLB查找 (虚拟地址 → 物理地址) Cycle 2: L1 Cache查找 (物理地址 → 数据) 如果TLB和Cache串行: → 每次访问至少2周期 → 即使Cache命中也有TLB延迟 优化: 虚拟索引/物理标签 (VIPT): → TLB和Cache索引同时进行 → Cache用虚拟地址的Index查数据 → 同时TLB翻译得到物理地址 → 比较物理Tag 条件: Index位必须在Offset内 → 4KB页: Offset=12位, 4路32KB: Index=8位 ✓ → 但4路64KB: Index=9位, 超出12位 ✗