第 9 章 存储管理单元设计
存储管理是现代操作系统的重要功能之一,其需要CPU硬件提供一定的支持,以软硬件协同的方式完成。CPU硬件中参与这一过程的逻辑通常被称为存储管理单元(Memory Management Unit,简称MMU)。截止目前,本书实践任务中所设计的CPU只实现了最简单的直接地址翻译模式。从这一章开始,我们将完成MMU其余部分的设计。这一部分的设计难点在于涉及的技术细节较多,初学者不容易分出主次。因此,我们将整个设计分为三个阶段,以供大家通过模块化的、循序渐进的方式来完成。
- 第一阶段:我们将专注于TLB模块自身的设计。
- 第二阶段:我们将TLB模块集成至已有的CPU中,并实现MMU相关指令和控制状态寄存器。
- 第三阶段:我们将MMU相关异常的支持添加完毕,进行全部功能的联合验证。
在开始这一阶段的设计之前,请先学习《计算机体系结构基础》(第3版)的3.3节或其他文献中的相关内容。学习的重点在于理解计算机系统中围绕MMU进行的软硬件交互过程。在掌握这个交互过程原理的基础之上,就比较容易把指令系统规范中关于MMU的相关定义串联成一个有机整体,进而进行CPU中相关功能的设计。
9.1 TLB模块设计分析
通过梳理TLB模块相关知识,并结合CPU流水线的结构特点,我们对TLB模块的设计分析如下:
1)TLB模块内部的主体应是一个二维组织结构的查找表。查找表的每一项分为两个部分,第一部分存储的信息既参与读写又参与查找比较,包括E、VPPN、PS、ASID和G;第二部分仅参与读写,包括PPN0、PLV0、MAT0、D0、V0、PPN1、PLV1、MAT1、D1、V1。查找表的项数由实现者自行定义。
2)TLB模块要支持取指和访存两个部分的虚实地址转换需求,即两部分都需要对TLB模块进行查找,且两部分对应的查找功能一致。查找时,需要向TLB模块输入s_vppn和s_asid信息,TLB模块输出的信息包含s_found、s_ppn、s_ps、s_plv、s_mat、s_d和s_v。其中输入的s_vppn来自访存虚地址的31..13位,s_asid来自CSR.ASID的ASID域。TLB输出的s_ppn和s_ps用于产生最终的物理地址,s_found的结果用于判定是否产生TLB重填异常,s_found和s_v结果用于判定是否产生页无效异常,s_found和s_plv用于判定是否产生页特权等级不合规异常,s_found、s_v和s_d结果用于判定是否产生页修改异常。
3)为了使流水线能够满负荷运转不断流,TLB模块要能够支持取指和访存同时进行查找,这意味着上面的查找端口应该有两套。
4)TLB模块需要支持TLBSRCH指令的查找操作。我们建议复用访存指令查找TLB的端口,即输入复用s_vppn和s_asid,输出复用已有的s_found。除此而外还需要一个额外的s_index输出,用于记录命中在第几项,其信息用于填入到CSR.TLBIDX中。
5)TLB模块需要支持TLBWR和TLBFILL指令的写操作。我们建议为此设计独立的端口。此时需要向TLB模块输入写地址w_index,写入的TLB表项信息w_e、w_vppn、w_ps、w_asid、w_g、w_ppn0、w_plv0、w_mat0、w_d0、w_v0、w_ppn1、w_mat1、w_mat1、w_d1、w_v1。因为是写操作,所以必须有一个写使能输入信号we。
6)TLB模块需要支持TLBRD指令的读操作。我们倾向于为此设计独立的端口。此时需要向TLB模块输入读地址r_index。TLB模块需要输出读的结果有r_e、r_vppn、r_ps、r_asid、r_g、r_ppn0、r_plv0、r_mat0、r_d0、r_v0、r_ppn1、r_plv1、r_mat1、r_d1、r_v1。
7)TLB模块需要支持INVTLB指令的查找、无效操作。其不同op对应的查找所用信息都可以复用访存指令查找TLB的端口,只是需要一个额外的invtlb_op输入,用于标识invtlb的具体操作类型。invtlb的无效操作都是在TLB模块内部根据查找结果直接将符合条件的TLB表项的E位置为0。
通过上述分析,我们得到TLB模块的接口与内部主要信号的定义如下:
module tlb
(
#parameter TLBNUM = 16
)
(
input wire clk,
input wire resetn,
// search port 0 (for fetch)
input wire [ 18:0] s0_vppn,
input wire [ 9:0] s0_asid,
output wire s0_found,
output wire [$clog2(TLBNUM)-1:0] s0_index,
output wire [ 19:0] s0_ppn,
output wire [ 5:0] s0_ps,
output wire [ 1:0] s0_mat,
output wire s0_d,
output wire s0_v,
// search port 1 (for load/store)
input wire [ 18:0] s1_vppn,
input wire [ 9:0] s1_asid,
output wire s1_found,
output wire [$clog2(TLBNUM)-1:0] s1_index,
output wire [ 19:0] s1_ppn,
output wire [ 5:0] s1_ps,
output wire [ 1:0] s1_mat,
output wire s1_d,
output wire s1_v,
// invtlb opcode
input wire invtlb_valid,
input wire [ 4:0] invtlb_op,
// write port
input wire we, //w(rite) e(nable)
input wire [$clog2(TLBNUM)-1:0] w_index,
input wire w_e,
input wire [ 18:0] w_vppn,
input wire [ 9:0] w_asid,
input wire w_g,
input wire [ 19:0] w_ppn0,
input wire [ 1:0] w_plv0,
input wire [ 1:0] w_mat0,
input wire w_d0,
input wire w_v0,
input wire [ 19:0] w_ppn1,
input wire [ 1:0] w_plv1,
input wire [ 1:0] w_mat1,
input wire w_d1,
input wire w_v1,
// read port
input wire [$clog2(TLBNUM)-1:0] r_index,
output wire r_e,
output wire [ 18:0] r_vppn,
output wire [ 5:0] r_ps,
output wire [ 9:0] r_asid,
output wire r_g,
output wire [ 19:0] r_ppn0,
output wire [ 1:0] r_plv0,
output wire [ 1:0] r_mat0,
output wire r_d0,
output wire r_v0,
output wire [ 19:0] r_ppn1,
output wire [ 1:0] r_plv1,
output wire [ 1:0] r_mat1,
output wire r_d1,
output wire r_v1
);
reg [TLBNUM-1:0] tlb_e;
reg [TLBNUM-1:0] tlb_ps4MB; //pagesize 1:4MB, 0:4KB
reg [ 18:0] tlb_vppn [TLBNUM-1:0];
reg [ 9:0] tlb_asid [TLBNUM-1:0];
reg tlb_g [TLBNUM-1:0];
reg [ 19:0] tlb_ppn0 [TLBNUM-1:0];
reg [ 1:0] tlb_plv0 [TLBNUM-1:0];
reg [ 1:0] tlb_mat0 [TLBNUM-1:0];
reg tlb_d0 [TLBNUM-1:0];
reg tlb_v0 [TLBNUM-1:0];
reg [ 19:0] tlb_ppn1 [TLBNUM-1:0];
reg [ 1:0] tlb_plv1 [TLBNUM-1:0];
reg [ 1:0] tlb_mat1 [TLBNUM-1:0];
reg tlb_d1 [TLBNUM-1:0];
reg tlb_v1 [TLBNUM-1:0];
......
endmodule
9.2 任务与实践
完成本章的学习后,希望读者能够完成以下3个实践任务:
- 设计TLB模块,参见下面第9.2.1小节。
- 在CPU中集成TLB模块并添加TLB相关指令和CSR寄存器,参见下面第9.2.2小节。
- 在CPU中完善TLB MMU功能并添加TLB相关例外支持,参见下面第9.2.3小节。
9.2.1 实践任务17:TLB模块设计
本实践任务要求如下:
- 设计TLB模块。
- 利用TLB模块级验证环境对所设计的TLB进行验证,通过仿真和上板验证。
请参照第2.3.1节中介绍的方式获取本次实践任务所需的实验开发环境。具体的实验环境与之前的环境不同,是针对TLB模块的单独验证环境,位于mycpu_env/module_verify/tlb_verify/ 目录下。具体目录结构及各部分功能简介如下所示:
|--tlb_verify/ 目录,TLB模块级验证环境。
| |--rtl/ 目录,包含TLB模块以及验证顶层的设计源码。
| | |--tlb_top.v TLB模块级验证的顶层文件。
| |--testbench/ 目录,包含功能仿真验证源码。
| | |--testbench.v 仿真顶层。
| |--run_vivado/ Vivado工程的运行目录。
| | |--constraints/ Vivado工程的设计约束。
| | |--tlb_prj/ Vivado工程文件所在目录。
实验环境准备就绪后,请参考下列步骤完成本实践任务:
- 完成TLB模块的设计和RTL编写,记为tlb.v,该模块名需要命名为“tlb”,输入/输出端口参见本章第9.1节。将tlb.v文件放入mycpu_env/myCPU/ 目录下。
- 进入 mycpu_env/module_verify/tlb_verify/run_vivado/tlb_prj/ 目录下启动验证tlb的工程。如果该目录下尚未创建工程,请参照附录D.2节介绍的步骤,利用该目录下的 create_project.tcl 文件创建工程。如需要,请参考附录D.4节进行IP核升级。
- 在验证tlb模块的工程中运行仿真(进入仿真界面后,直接点击run all),进行功能验证与调试,直至仿真测试通过。
- 在验证tlb模块的工程中综合实现后生成bit流文件,进行上板验证。(如果无硬件实验平台,请跳过该步骤。)
9.2.1.1 仿真验证结果判断
在仿真时,会有16次写,16次读以及26次查操作,所有操作都完成后会打印PASS,如下所示:
[ 2705 ns] OK!!!write
…………
=========================================================
Test end!
----PASS!!!
如果仿真中发现错误,请进行调试。这时需要观察TLB接口的访问,了解该次请求的效果,然后查看TLB的读出数据是否与预期效果相同。
9.2.1.2 上板验证结果判断
正确的上板运行效果如图9.1所示。
第一阶段上板运行时,应看到数码管发生如下变化:
- 首先是写操作(W),最右侧的数码管会从0x00累加到0x0f,此后最右侧那个单色LED灯亮起,表示写操作完成。
- 之后是同时进行读操作和查找操作,相应的数码管也会开始累加:
- 对于读操作(R),会进行16次读,次右侧的数码管会从0x00累加到0x0f。
- 对于0号查找操作(S0),会进行13次查找(查偶数次请求),次左侧的数码管会以步长2从0x00加到0x18,也就是0、2、4、……、0x18。
- 对于1号查找操作(S1),会进行13次查找(查奇数次请求),最左侧的数码管会以步长2从0x01加到0x19,也就是1、3、5、……、0x19。
- 第2步中的累加完成后,LED的右侧三个灯全部亮起,表明测试完成。此时正确的数码管显示是0x19180f0f。如果数码管停在其他数值上,表示上板失败。
9.2.2 实践任务18:添加TLB相关指令和CSR寄存器
本实践任务要求在实践任务16和实践任务17的基础上完成以下工作:
- 将实践任务17完成的TLB模块集成到实践任务16完成的CPU中。
- 在CPU中增加TLBSRCH、TLBRD、TLBWR、TLBFILL、INVTLB指令。
- 在CPU中增加TLBIDX、TLBEHI、TLBELO0、TLBELO1、ASID、TLBRENTRY CSR寄存器。
- 在采用AXI总线的SoC验证环境里完成exp18对应func的功能验证,要求成功通过仿真和上板验证。
请参照第2.3.1节中介绍的方式获取本次实践任务所需的实验开发环境。具体的实验环境仍位于 mycpu_env/ 目录下,且仍使用 soc_axi/ 子目录。
实验环境准备就绪后,请参考下列步骤完成本实践任务:
- 将所实现CPU的代码更新至mycpu_env/myCPU/目录中。
- 修改func配置文件——mycpu_env/func/include/test_config.h,选择exp18的配置,编译。(如果是通过压缩包exp18.zip获取实验开发环境的,请跳过该步骤。)
- 打开gettrace工程——mycpu_env/gettrace/gettrace.xpr。(该Vivado工程中的IP核是使用Vivado2019.2创建的,如果使用更高版本的Vivado打开,请参考附录D.4节进行IP核升级。)运行gettrace工程的仿真(进入仿真界面后,直接点击run all等待仿真运行完成),生成新的参考trace文件golden_trace.txt(mycpu_env/gettrace/golden_trace.txt)。要等仿真运行完成,golden_trace.txt才有完整的内容。(如果是通过压缩包exp18.zip获取实验开发环境的,请跳过该步骤。)
- 进入 mycpu_env/soc_verify/soc_axi/run_vivado/ 目录下启动验证myCPU的工程。如果该目录下尚未创建工程,请参照附录D.2节介绍的步骤,利用该目录下的 create_project.tcl 文件创建工程。如需要,请参考附录D.4节进行IP核升级。如果该目录下已有前一实践任务创建过的工程,可以在打开工程后,参照附录D.3节介绍的步骤,更新项目中CPU实现文件的列表。
- 参考第4章4.2.5.2小节,对工程中的axi_ram重新定制。(如果是通过压缩包exp18.zip获取实验开发环境的,请跳过该步骤。)
- 在验证myCPU的工程中运行仿真(进入仿真界面后,直接点击run all),进行功能验证与调试,直至仿真测试通过。
- 在验证myCPU的工程中综合实现后生成bit流文件,进行上板验证。(如果无硬件实验平台,请跳过该步骤。)
9.2.3 实践任务19:添加TLB相关例外支持
本实践任务要求在实践任务18所实现CPU的基础上完成以下工作:
- 为CPU增加TLB相关异常:TLB重填例外、load/store/取指操作页无效例外、页修改例外、页特权等级不合规例外。
- 在CPU中增加DMW CSR寄存器。
- 为CPU增加虚实地址映射的功能。
- 在采用AXI总线的SoC验证环境里完成exp19对应func的功能验证,要求成功通过仿真和上板验证。
请参照第2.3.1节中介绍的方式获取本次实践任务所需的实验开发环境。具体的实验环境仍位于 mycpu_env/ 目录下,且仍使用 soc_axi/ 子目录。
实验环境准备就绪后,请参考下列步骤完成本实践任务:
- 将所实现CPU的代码更新至mycpu_env/myCPU/目录中。
- 修改func配置文件——mycpu_env/func/include/test_config.h,选择exp19的配置,编译。(如果是通过压缩包exp19.zip获取实验开发环境的,请跳过该步骤。)
- 打开gettrace工程——mycpu_env/gettrace/gettrace.xpr。(该Vivado工程中的IP核是使用Vivado2019.2创建的,如果使用更高版本的Vivado打开,请参考附录D.4节进行IP核升级。)运行gettrace工程的仿真(进入仿真界面后,直接点击run all等待仿真运行完成),生成新的参考trace文件golden_trace.txt(mycpu_env/gettrace/golden_trace.txt)。要等仿真运行完成,golden_trace.txt才有完整的内容。(如果是通过压缩包exp19.zip获取实验开发环境的,请跳过该步骤。)
- 进入 mycpu_env/soc_verify/soc_axi/run_vivado/ 目录下启动验证myCPU的工程。如果该目录下尚未创建工程,请参照附录D.2节介绍的步骤,利用该目录下的 create_project.tcl 文件创建工程。如需要,请参考附录D.4节进行IP核升级。如果该目录下已有前一实践任务创建过的工程,可以在打开工程后,参照附录D.3节介绍的步骤,更新项目中CPU实现文件的列表。
- 参考第4章4.2.5.2小节,对工程中的axi_ram重新定制。(如果是通过压缩包exp19.zip获取实验开发环境的,请跳过该步骤。)
- 在验证myCPU的工程中运行仿真(进入仿真界面后,直接点击run all),进行功能验证与调试,直至仿真测试通过。
- 在验证myCPU的工程中综合实现后生成bit流文件,进行上板验证。(如果无硬件实验平台,请跳过该步骤。)