设备和输入输出

你已经在NEMU和NPC上都实现TRM了, 下一步当然是让它们支持输入输出了.

在NEMU中实现输入输出

根据PA讲义完成PA2阶段3, 直到你看到如下提示框:

温馨提示

PA2到此结束...

NPC中的输入输出

对于RISC-V架构, 输入输出是通过MMIO来实现的. 有了基于DPI-C的内存读写函数, 目前我们不必修改RTL代码就可以为NPC实现输入输出了! 我们只需要在这两个函数中对地址的范围进行简单的判断, 就可以将来自NPC的访存请求重定向到正确的设备了. 硬件上的MMIO是基于总线来实现的, 我们将来再实现真正的MMIO.

关于设备, 我们在这里不直接采用NEMU的设备模型, 而是为NPC在仿真环境中实现一套与将来流片SoC相近的设备模型. 实现总线后, 我们再来基于总线实现RTL版本的设备. 这时候, AM中IOE抽象的作用就体现出来了: NEMU和NPC的设备地址和设备模型都有所不同, 但经过抽象之后, 它们都可以运行同一份红白机模拟器的源代码, 更多地, AM上的所有程序都不必为不同的运行环境编写不同的代码.

在NPC中运行超级玛丽

大家已经完成了支持RV32IM和外设的NEMU, 并在NEMU上成功运行了超级玛丽. 同样, 我们也可以在RV32E的NPC上运行超级玛丽, 不过这首先需要实现串口和时钟.

我们知道RISC-V处理器通过MMIO访问外设, 例如在NEMU中串口会映射到0xa00003f8. 类似地, 我们也可以在NPC的仿真环境中实现简单的串口和时钟. 在上一节中提到, 我们通过DPI-C方式让NPC调用读写函数pmem_read()pmem_write()来访问内存. 和NEMU一样, 我们可以在这两个函数中添加若干判断来实现MMIO的功能, 伪代码如下所示:

extern "C" int pmem_read(int raddr) {
  // 总是读取地址为`raddr & ~0x3u`的4字节并返回
  if (raddr == 时钟地址) { 返回当前时间 }
  ...
}
extern "C" void pmem_write(int waddr, int wdata, char wmask) {
  // 总是往地址为`waddr & ~0x3u`的4字节按写掩码`wmask`写入`wdata`
  // `wmask`中每比特表示`wdata`中1个字节的掩码,
  // 如`wmask = 0x3`代表只写入最低2个字节, 内存中的其它字节保持不变
  if (waddr == 串口地址) { putchar(...) }
  ...
}

在实现串口和时钟之后, 你可以运行相应的AM程序来测试, 并尝试在NPC上运行字符版本的超级玛丽.

为NPC添加串口和时钟

  • 在NPC仿真环境中实现串口的输出功能, 并运行hello程序.
  • 在NPC仿真环境中实现时钟, 并运行am-testsreal-time clock test测试. 可以基于系统时间来实现时钟的功能, 在C语言中与系统时间相关的库是什么, 以及如何获取系统时间, 就交给你来STFW了.
  • 运行字符版本的红白机模拟器.

若你有兴趣还可以仿照NEMU实现VGA, 并在NPC上运行图形版本的超级玛丽.

运行图形版本的超级玛丽

  • 在NPC仿真环境中实现VGA外设, 运行video测试.
  • 运行图形版本的红白机模拟器.
最近更新时间:
贡献者: Zihao Yu