一、项目背景
在敏捷开发模式定义中,通过功能建模(NEMU),性能建模(GEM5),真实建模(RTL)来完成 CPU 项目迭代。本节从宏观上介绍 NEMU 模拟器项目架构,通过一个思考流,串联 NEMU 模拟器在实际业务开发中的场景。
在实际场景中,NEMU 的作用更多的是作为一个"参考模型",在交付 CPU 处理器验证的环境下运行客户负载程序,对比实际模型的执行结果是否符合预期。那么它的作用如下图所示:
二、总体开发框架介绍
同上一节的 AM 一样,NEMU 模拟器也提供了抽象的能力,对于不同架构的处理器定义,通过不同的 ISA 指令集行为模拟来完成对应架构的功能描述,这意味着当 RV 要支持新的,比如 V 拓展,H 拓展,矩阵算子等拓展,可以通过更新相应的行为模拟实现来完成。
下面我们通过模拟一个需求开发来串联下 NEMU 使用的情况。
事件1.项目经理提出了 NEMU 模拟器需要支持 V 向量拓展,NEMU 模拟器需要完成相应的支持。
事件2.调研 V 拓展的手册定义,制定一个将手册实现映射到模拟器的计划。
事件3.构建 RVV 的实现的验证环境,发现 Spike 支持 Rvv,构建功能验证计划。
事件4.进行功能实现并迭代验证,交付给 RTL 模型,在实际业务中验证功能。
那么你可能会对这几个部分做增量修改
那么你可能更改的文件是
src/isa/riscv64/├── insn/ # 指令实现目录│ ├── vadd_vv.c # 手动编写的向量加法指令│ ├── vmul_vx.c # 手动编写的向量乘法指令│ └── ...├── decode.c # 手动修改译码逻辑├── reg.c # 手动扩展寄存器└── inst.c # 手动添加指令表
你可能搭建的验证环境是
三、源码解析
我们先在宏观上看一下项目的架构
nemu├── src│ ├── monitor/ # 监控器和调试器核心│ │ └── sdb/ # 简易调试器 (SDB) 实现│ ├── isa/ # ISA相关实现│ │ ├── x86/│ │ ├── mips32/│ │ ├── riscv32/│ │ └── riscv64/ # 例如,指令实现、寄存器定义│ ├── cpu/ # CPU模拟核心框架│ ├── memory/ # 内存访问实现│ ├── device/ # 设备模拟│ └── engine/ # 模拟器执行引擎├── include/ # 头文件│ ├── isa.h # ISA相关API声明│ ├── cpu/│ └── ...└── tools/kconfig/ # 配置系统
其中如果我们第一次看源码的话,可以从入口看起
(1)NEMU 程序的入口函数:
int main(int argc, char *argv[]) { // ... init_monitor(argc, argv); // 初始化监控器 engine_start(); // 启动模拟引擎 return is_exit_status_bad();}
其中 init_monitor 主要是用来在软件 Debug 中调试的,当实现模拟器出现问题后,可以通过该函数来尝试到报错现场附件,打印相关信息调试。
(2)CPU 指令执行循环
void cpu_exec(uint64_t n) { // ... 状态检查和时间记录 execute(n); // ... 后续处理}
模拟器中使用 cpu_exec 来定义完成一条指令的标准,当指令的行为描述已经更新对应的状态寄存器后,模拟器会驱动该函数执行以进行下一轮执行。
(3)内存访问模拟
对于内存的访问判断伪代码如下图所示
// 模拟的内存数组uint8_t pmem[PMEM_SIZE];// 统一的虚拟地址读取函数uint64_t vaddr_read(vaddr_t addr, int len) { // 1. 地址转换 (如果MMU开启,会经过TLB、页表等流程,将虚拟地址转换为物理地址paddr) paddr_t paddr = addr_translate(addr, len, MEM_TYPE_READ); // 2. 判断是否是内存映射I/O (MMIO) 区域 if (is_mmio(paddr)) { // 是MMIO地址,调用设备读取方法 return mmio_read(paddr, len); } else { // 是物理内存地址,直接从模拟内存pmem中读取 return pmem_read(paddr, len); }}
(4)外设访问模拟
对于外设访问,在模拟器中用简单的字符打印替代(Uart-lite)
// 示例:串口设备写入函数void serial_write(paddr_t paddr, uint64_t data, int len) { // 假设SERIAL_PORT_BASE是数据端口,将数据写入串口 // 这里只是简单地将字符输出到标准输出 putchar((char)(data & 0xFF)); // 只取最低字节 fflush(stdout); // 刷新输出缓冲区}
四、总结
NEMU 作为敏捷开发中的关键功能建模工具,提供了灵活的 ISA 扩展能力和完整的模拟环境,为 CPU 验证提供了可靠的参考基准,支撑从功能模拟到真实硬件的完整开发流程。
点击下方图标,关注开芯院平台
▼
