第23课 · 虚拟内存
页表TLB地址翻译
📌 学习目标:理解虚拟内存和页式地址翻译的原理,实现简化的 TLB,通过 Verilator 验证虚拟地址到物理地址的翻译。
一、为什么需要虚拟内存?
- 隔离:每个进程拥有独立的地址空间
- 扩展:虚拟地址空间可大于物理内存
- 简化:程序使用连续虚拟地址,物理内存可离散分配
二、页式地址翻译
虚拟地址: [VPN(20bit)] [Offset(12bit)]
↓ 页表查找
物理地址: [PPN(20bit)] [Offset(12bit)]
页大小 = 4KB (2^12)
PTE: [Valid(1)] [PPN(20)] [R(1)] [W(1)] [X(1)]
三、TLB(Translation Lookaside Buffer)
TLB 是页表的 Cache:
TLB 命中 → 1 周期得到物理地址
TLB 缺失 → 查页表 → 填充 TLB → 重试
典型 TLB 命中率: >99%
四、简化设计参数
- 虚拟地址:16 bit(简化)
- 页大小:256 字节 → Offset 8 bit → VPN 8 bit
- TLB:4 项全相联
Verilog 实现
"keyword">module tlb_simple (
"keyword">input clk, rst,
"keyword">input [15:0] vaddr,
"keyword">input translate_en,
"keyword">output "keyword">reg [15:0] paddr,
"keyword">output "keyword">reg tlb_hit, tlb_miss, page_fault
);
"keyword">parameter TLB_ENTRIES = 4;
"keyword">parameter VPN_BITS = 8;
"keyword">parameter PPN_BITS = 8;
"keyword">parameter OFFSET_BITS = 8;
"keyword">reg [VPN_BITS-1:0] tlb_vpn [0:TLB_ENTRIES-1];
"keyword">reg [PPN_BITS-1:0] tlb_ppn [0:TLB_ENTRIES-1];
"keyword">reg tlb_valid [0:TLB_ENTRIES-1];
"keyword">reg [29:0] page_table [0:255];
"keyword">reg [1:0] replace_ptr;
"keyword">wire [VPN_BITS-1:0] vpn = vaddr[15:OFFSET_BITS];
"keyword">wire [OFFSET_BITS-1:0] offset = vaddr[OFFSET_BITS-1:0];
"keyword">integer i;
"keyword">reg found; "keyword">reg [PPN_BITS-1:0] found_ppn;
"keyword">always @(*) "keyword">begin
found = 0; found_ppn = 0;
"keyword">for (i = 0; i < TLB_ENTRIES; i = i + 1)
"keyword">if (tlb_valid[i] && tlb_vpn[i] == vpn) "keyword">begin found = 1; found_ppn = tlb_ppn[i]; "keyword">end
"keyword">end
"keyword">wire pte_valid = page_table[vpn][29];
"keyword">wire [PPN_BITS-1:0] pte_ppn = page_table[vpn][28:21];
"keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin
"keyword">if (rst) "keyword">begin
"keyword">for (i=0; i"keyword">begin tlb_valid[i]<=0; tlb_vpn[i]<=0; tlb_ppn[i]<=0; "keyword">end
replace_ptr<=0; tlb_hit<=0; tlb_miss<=0; page_fault<=0; paddr<=0;
"keyword">end "keyword">else "keyword">begin
tlb_hit<=0; tlb_miss<=0; page_fault<=0;
"keyword">if (translate_en) "keyword">begin
"keyword">if (found) "keyword">begin paddr<={found_ppn,offset}; tlb_hit<=1; "keyword">end
"keyword">else "keyword">begin
tlb_miss<=1;
"keyword">if (pte_valid) "keyword">begin
tlb_vpn[replace_ptr]<=vpn; tlb_ppn[replace_ptr]<=pte_ppn;
tlb_valid[replace_ptr]<=1; replace_ptr<=replace_ptr+1;
paddr<={pte_ppn,offset};
"keyword">end "keyword">else page_fault<=1;
"keyword">end
"keyword">end
"keyword">end
"keyword">end
"keyword">endmodule
测试台
"keyword">module tb_tlb_simple;
"keyword">reg clk,rst; "keyword">reg [15:0] vaddr; "keyword">reg translate_en;
"keyword">wire [15:0] paddr; "keyword">wire tlb_hit,tlb_miss,page_fault;
tlb_simple uut(.*);
"keyword">integer pass=0,fail=0;
"keyword">always #5 clk=~clk;
"keyword">initial "keyword">begin
clk=0;rst=1;vaddr=0;translate_en=0;
#12;rst=0;
uut.page_table[0]=30'h20000005; // V=1,PPN=5
uut.page_table[1]=30'h20000003; // V=1,PPN=3
uut.page_table[2]=30'h0; // V=0
vaddr=16'h0000; translate_en=1; #10;
"keyword">if (tlb_miss && paddr===16'h0500) pass=pass+1; "keyword">else "keyword">begin $display("FAIL1"); fail=fail+1; "keyword">end
translate_en=0; #10;
vaddr=16'h0000; translate_en=1; #10;
"keyword">if (tlb_hit && paddr===16'h0500) pass=pass+1; "keyword">else "keyword">begin $display("FAIL2"); fail=fail+1; "keyword">end
translate_en=0; #10;
vaddr=16'h0200; translate_en=1; #10;
"keyword">if (page_fault) pass=pass+1; "keyword">else "keyword">begin $display("FAIL3"); fail=fail+1; "keyword">end
translate_en=0; #10;
$display("========================================");
$display("TLB+页表测试: PASS=%0d FAIL=%0d",pass,fail);
"keyword">if (fail==0) $display("✅ 虚拟内存地址翻译全部正确!"); "keyword">else $display("❌ 存在失败!");
$display("========================================"); $finish;
"keyword">end
"keyword">endmodule
Verilator 编译与运行
verilator --cc tlb_simple.v --exe tb_tlb_simple.v --build --top-module tb_tlb_simple
./obj_dir/Vtb_tlb_simple
📌 扩展阅读
本课的核心概念在实际工程中有广泛应用:
- 在芯片设计中,模块化设计方法至关重要——每个 IP 核都有清晰的接口定义
- 仿真验证是确保设计正确性的关键步骤,Verilator 是工业界广泛使用的开源工具
- 时序约束和综合优化直接影响芯片的性能和功耗表现
- 形式验证和断言检查可以显著提高设计可靠性,减少流片后的 bug
🔧 调试技巧
在开发本课模块时,常见问题和解决方法:
- 仿真不收敛:检查组合逻辑是否形成了环路(combinational loop)
- 时序违例:添加流水线寄存器切断关键路径,或降低工作频率
- 资源超标:使用资源共享、时分复用等技术减少硬件开销
- 功能不正确:先写测试台(testbench),用断言检查关键信号
📊 性能指标
衡量本课模块性能的关键指标:
| 指标 | 含义 | 目标 |
| 延迟(Latency) | 从输入到输出的周期数 | 尽可能小 |
| 吞吐量(Throughput) | 每周期处理的数据量 | 尽可能大 |
| 面积(Area) | 占用的 LUT/FF 资源 | 在性能满足下最小化 |
| 功耗(Power) | 动态 + 静态功耗 | 在性能满足下最小化 |
🔗 与其他课程的联系
本课内容在整个 RISC-V 数字电路课程中的位置:
- 第01-05课的组合逻辑是所有硬件模块的基础
- 第06-10课的时序逻辑提供了寄存器和状态机的设计方法
- 第11-20课的CPU设计是 SoC 的核心处理单元
- 第21-23课的存储层次为数据访问提供速度和容量平衡
- 第24-28课的异常与中断使系统能响应外部事件
- 第29-33课的外设接口连接 CPU 与外部世界
- 第34-37课的系统集成将所有模块整合为完整 SoC
- 第38-40课的验证与毕业确保设计正确可靠
🏆 成就解锁:虚拟内存
✅ Verilator 仿真验证通过
✅ TLB 全相联查找逻辑正确
✅ 页表地址翻译正确
✅ TLB 缺失填充正确
✅ 页错误检测正确
🤔 思考题
1. 本课设计的模块如何与前面课程的内容结合?
2. 修改参数后,系统的行为会有什么变化?