首页 > 最新动态 > [香山开源开源代码介绍 02 ] 如何给 CPU 建模工具加新 feature
最新动态
[香山开源开源代码介绍 02 ] 如何给 CPU 建模工具加新 feature
2025-11-189
图片


一、项目背景


在敏捷开发模式定义中,通过功能建模(NEMU),性能建模(GEM5),真实建模(RTL)来完成 CPU 项目迭代。本节从宏观上介绍 NEMU 模拟器项目架构,通过一个思考流,串联 NEMU 模拟器在实际业务开发中的场景。

在实际场景中,NEMU 的作用更多的是作为一个"参考模型",在交付 CPU 处理器验证的环境下运行客户负载程序,对比实际模型的执行结果是否符合预期。那么它的作用如下图所示:

img_v3_02rp_a3f69d94-759e-4f06-b720-1bb0c35c4d4g.jpg


二、总体开发框架介绍



同上一节的 AM 一样,NEMU 模拟器也提供了抽象的能力,对于不同架构的处理器定义,通过不同的 ISA 指令集行为模拟来完成对应架构的功能描述,这意味着当 RV 要支持新的,比如 V 拓展,H 拓展,矩阵算子等拓展,可以通过更新相应的行为模拟实现来完成。

下面我们通过模拟一个需求开发来串联下 NEMU 使用的情况。

事件1.项目经理提出了 NEMU 模拟器需要支持 V 向量拓展,NEMU 模拟器需要完成相应的支持。

事件2.调研 V 拓展的手册定义,制定一个将手册实现映射到模拟器的计划。

事件3.构建 RVV 的实现的验证环境,发现 Spike 支持 Rvv,构建功能验证计划。

事件4.进行功能实现并迭代验证,交付给 RTL 模型,在实际业务中验证功能。

那么你可能会对这几个部分做增量修改

22.jpg


那么你可能更改的文件是

src/isa/riscv64/├── insn/                    # 指令实现目录│   ├── vadd_vv.c           # 手动编写的向量加法指令│   ├── vmul_vx.c           # 手动编写的向量乘法指令│   └── ...├── decode.c                # 手动修改译码逻辑├── reg.c                   # 手动扩展寄存器└── inst.c                  # 手动添加指令表

你可能搭建的验证环境是

33.png




三、源码解析



我们先在宏观上看一下项目的架构

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 验证提供了可靠的参考基准,支撑从功能模拟到真实硬件的完整开发流程。



点击下方图标,关注开芯院平台

·
图片



·




点我访问原文链接