Hint: this post is also available in English.

上期当中的一个结论就是,为了在Pano Logic G1上使用32位的LPDDR内存,必须需要使用缓存。当然我可以使用16位模式,一半的容量限制(16MB)对于我来说也确实不是什么大问题……但是我还是决定实现一个缓存,应该不会很难吧?

于是结果就是,缓存实现了,32位DDR也可以用了。我实现的是一个8KB 2路组相关缓存,替换策略是LRU(2路情况下LRU实现很容易),写入策略是写回(实现cache的一大原因就是因为 没有了数据mask之后基本不可能实现write-through)。缓存现在连接在PicoRV32 CPU和MIG内存控制器之间,所以所有到LPDDR的读写都是经过缓存的。关于缓存具体的细节我这里就不多说了,因为我觉得没有什么特别的,除了我的实现可能特别烂……以后需要加入GameBoy CPU的时候我会在中间再加一个总线仲裁器,这样两个CPU就可以共享内存了。尽管需要注意的是,这只是一个2路组相关内存,如果总线上有多个主机共享这个缓存,大概性能会非常成问题。

性能方面:

  • 读取命中: 2 周期
  • 写入命中: 2 周期
  • 读取未命中: 4 周期 + 内存读取延迟
  • 写入未命中: 5 周期 + 内存读取延迟
  • 读取未命中 + 冲刷: 12 周期 + 内存写入延迟 + 内存读取延迟
  • 写入未命中 + 冲刷: 13 周期 + 内存写入延迟 + 内存读取延迟

可以看到,未命中的代价还算很高的,而冲刷的成本则是……非常高。

以及,因为我的代码太烂加上Spartan-3E的BlockRAM的限制(不支持字节使能,对于一个需要字节使能的缓存而言相当重要),想比于无缓存16位的版本,整个设计用了1500多个LUT。我估计大部分来自于缓存,小部分来自于32位的内存控制器。

但是无论如何... 又不是不能用™。

1553534933865-2216511551544662520.jpg

如何使用

确保电脑上安装了g++,riscv-unknown-elf-gcc和ISE 14.7。RV32 gcc需要支持march=rv32i。

Clone VerilogBoy的GitHub源,检出commit b08377d (Merge branch 'refactor').

运行如下的命令:

cd tools/bin2mif
g++ -o bin2mif bin2mif.cpp
cd ../../target/panog1/fw
make
cp *.mif ../fpga

进入 target/panog1/fpga,用ISE 14.7打开工程 pano_top.xise 并生成编程文件。