RISC-V架构下计算机系统基础实验环境搭建与指令模拟
1. 项目概述南京大学计算机系统基础课程的PA实验Programming Assignment系列是计算机科学与技术系最具挑战性的实践环节之一。2022年秋季学期的PA实验采用RISC-V 32位架构作为基础平台配合Ubuntu 20.04 LTS开发环境引导学生从零开始构建完整的计算机系统认知体系。这个系列实验最独特之处在于其自底向上的设计理念从最基本的硬件抽象层开始逐步实现指令集、操作系统内核、编译器直至应用程序的完整栈。PA0和PA1作为整个系列的开篇主要聚焦环境搭建和基础指令模拟为后续更复杂的系统组件开发奠定基础。2. 实验环境准备2.1 开发环境配置实验官方推荐使用Ubuntu 20.04 LTS作为基础系统这是考虑到其长期支持特性和软件包稳定性。实际配置时需要特别注意以下组件版本gcc 9.4.0 make 4.2.1 qemu 4.2.1 riscv32-unknown-elf-gcc 8.3.0安装这些依赖时建议使用apt的固定版本安装命令sudo apt install gcc-9 g-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90注意Ubuntu 22.04默认的gcc-11可能导致某些RISC-V工具链兼容性问题这是PA实验环境配置中最常见的坑。2.2 实验代码结构PA实验采用模块化代码组织方式核心目录结构如下pa/ ├── include/ # 公共头文件 ├── lib-common/ # 公共库 ├── pa0/ # 第0个实验 ├── pa1/ # 第1个实验 └── tools/ # 工具链每个PA子项目都包含完整的Makefile构建系统支持以下关键命令make clean # 清理构建产物 make build # 编译项目 make test # 运行测试用例 make submit # 生成提交包3. PA0实验详解3.1 实验目标与设计PA0作为整个系列的热身实验主要实现三个核心目标搭建可工作的RISC-V开发环境理解基础指令执行流程实现简易的寄存器状态显示实验模拟器采用C语言编写核心架构是经典的取指-译码-执行循环while (1) { uint32_t instr instr_fetch(pc, 4); decoding_exec(instr); update_pc(); }3.2 关键实现细节寄存器文件的实现采用了union结构体既能以32位整型访问也能按字节单独操作typedef union { struct { uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; }; uint32_t word; } reg_t;指令模拟部分需要特别注意RISC-V的小端序特性。例如lw指令的加载实现uint32_t val 0; for (int i 0; i 4; i) { val | (uint32_t)pmem[addr i] (8 * i); }实操技巧使用gdb调试时可以通过display /x $pc命令持续监视程序计数器变化这对理解指令流非常有帮助。4. PA1实验深入解析4.1 指令集扩展实现PA1在PA0基础上增加了20条基础指令的模拟主要包括算术指令ADD/SUB/SLT/SLTU逻辑指令AND/OR/XOR移位指令SLL/SRL/SRA内存指令LW/SW控制指令BEQ/BNE/JAL指令编码解析采用RISC-V标准格式uint8_t opcode instr 0x7f; uint8_t rd (instr 7) 0x1f; uint8_t funct3 (instr 12) 0x7;4.2 异常处理机制PA1引入了基础的异常处理框架需要实现非法指令检测内存越界检查控制台输入输出模拟异常处理流程的关键代码结构if (opcode ILLEGAL_INSTR) { raise_exception(EX_ILLEGAL_INSTR, pc); return; }内存访问检查示例bool in_pmem (addr PMEM_LEFT) (addr PMEM_RIGHT); if (!in_pmem) { raise_exception(EX_MEM_FAULT, pc); return; }5. 调试与测试技巧5.1 常见问题排查指令执行顺序错误检查PC更新逻辑是否正确验证条件跳转的标志位计算内存访问异常确认物理内存范围检查检查字节序转换实现寄存器值异常验证寄存器索引范围检查符号扩展实现5.2 自动化测试方法实验提供了完善的测试框架可以通过以下方式运行make test TESTadd # 单个测试用例 make test-all # 完整测试集测试用例的黄金参考golden reference存放在tests/目录下包含寄存器状态快照内存内容转储控制台输出记录6. 实验进阶建议完成基础要求后可以尝试以下扩展实现指令流水线可视化添加CSR寄存器支持引入中断处理机制支持更多的RV32I指令例如实现简单的流水线显示void print_pipeline() { printf([FETCH] 0x%08x\n, fetch_stage.pc); printf([DECODE] %s\n, decode_stage.instr_name); printf([EXEC] R%d0x%x\n, exec_stage.rd, exec_stage.result); }我在实际完成PA1时发现RISC-V的立即数编码方式特别容易出错。比如J型指令的立即数需要将20位立即数拆分为多个字段后重新组合。建议单独编写一个立即数解码测试工具这在调试跳转指令时能节省大量时间。