设备和输入输出

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

在NEMU中实现输入输出

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

温馨提示

PA2到此结束...

NPC中的输入输出

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

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

在NPC中运行超级马里奥

经过前面的披荆斩棘,同学们已经完成了支持RV64IM和外设的NEMU,并在NEMU上运行了超级马里奥,同时你们还完成了支持RV64IM的NPC,那么你是否也想在自己的NPC上运行超级马里奥呢?我们就将实现这个目标作为B线的收尾吧。

要在NPC上运行超级马里奥首先需要实现uart和rtc外设。我们知道计算机中是通过mmio的方式来访问外设的,例如在NEMU中串口会映射到0xa00003f8。现在我们要在NPC的仿真环境中也实现一个简单的uart和rtc外设。在B阶段的“支持RV64IM的单周期NPC”章节中提到,我们是用dpi-c的方式来让NPC访问内存,为此我们定义了内存读写函数pmem_read和pmem_write,要实现内存映射只需要在这两个函数中添加一些判断分支即可,伪代码如下所示:

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

rtc实现的提示

rtc的实现可以暂时用系统时间来代替,在C语言中与系统时间相关的库是什么,以及如何获取系统时间,就交给你来STFW了。

在实现了uart和rtc外设之后你可以运行am-test中的hello测试和rtc测试来看看效果,这两个实现正确后就可以编译字符版的超级马里奥,并在NPC上运行。

为NPC添加uart和rtc外设

  • 在NPC仿真环境中实现uart外设,并运行hello测试。
  • 在NPC仿真环境中实现rtc外设,并运行rtc测试。
  • 运行超级马里奥。

若你有兴趣还可以仿照NEMU实现vga,并在NPC上运行彩色的超级马里奥。

运行彩色版的超级马里奥

  • 在NPC仿真环境中实现vga外设,运行video测试。
  • 运行彩色版的超级马里奥。
最近更新时间:
贡献者: Zihao Yu