FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages/

Freedom E300 Everywhere を改造して外部SRAMからプログラムをロードして実行する

RISC-V の実装であるFreedom E300 Everywhere を改造して、外部にSRAMを接続してそこにプログラムを置き、RISC-V Rocket-Chipからプログラムをフェッチして実行できるようにする。

オリジナルの構成では、Freedom E300 EverywhereではQSPIが乗っていた部分を取り払って、SimpleSRAMというChiselで記述したモジュールを置き、そこにプログラムを配置してRISC-V Rocket-Coreからフェッチしてもらう。

f:id:msyksphinz:20180221011128p:plain
図. Freedom E300 Everywhereの構成図。QSPIの部分を取り払い、SRAMを配置する。今回はこの場所にプログラムを置き、実行してみる。

RISC-Vプログラムの記述

あまり詳しいことはよくわかっていないが、 Freedom E300 Everywhereに搭載されているRocket-Chipは以下の2ステップでプログラムを実行するようになっている。

  1. リセット後は0x10000に配置されたBootROMから初期プログラムをフェッチする(だいたい、外部メモリ領域へのメモリフェッチが入るような、ジャンプコードが入っている)
  2. 外部AXIメモリやDDR3-SDRAMなどの領域から、プログラムをフェッチする。

オリジナルのE300では、QSPIの領域(0x2000_0000)にジャンプし、Linuxなりベアメタルなりのプログラムをロードするようになっている訳だ。

この部分にSimpleSRAMという自作のChiselモジュールを置き、RAMの部分を$readmemhでhexファイルをロードして、プログラムを読み込めるようにしよう。

  • 記述したプログラム (simple_main.c) ※ ちょっとだけFizzBuzzっぽい。
int simple_main ()
{

  (*(volatile unsigned int *)0x20000100) = 0xdeadbeef;

  int total = 0;
  for (int i = 0; i < 100; i++) {
        if ((i % 3) == 0) total += 3;
        if ((i % 5) == 0) total += 5;
        total += i;
  }

  (*(volatile int *)0x20000104) = total;

  return 0;
}

これをコンパイルし、SimpleSRAMにロードするようにしておく。

  initial begin
    $readmemh("../../../program/simple_main/simple_main.hex", tb_sifive_freedom.duv.simpleRAMs_0.memory);
  end

注意点1. SimpleSRAMに接続されるTileLinkはExecutable属性を付けておく

TileLinkの実装に慣れないと、本当にどこで設定されているのかわからない。SimpleSRAMのTileLinkの設定で、executable属性をtrueにしておく。

  • freedom/rocket-chip/src/main/scala/devices/tilelink/SimpleSRAM.scala
    Seq(TLManagerParameters(
      address            = AddressSet.misaligned(c.address, c.depth*beatBytes),
      resources          = new SimpleDevice("ram", Seq("msyksphinz,simpleRAM")).reg("mem"),
      regionType         = RegionType.UNCACHEABLE,
      executable         = true,
      supportsGet        = TransferSizes(1, beatBytes),
      supportsPutPartial = TransferSizes(1, beatBytes),
      supportsPutFull    = TransferSizes(1, beatBytes),
      fifoId             = Some(0))), // requests are handled in order

注意点2. SimpleSRAMを$readmemhを使用する場合は、RANDOMIZE_MEM_INIT define属性を解除する

これを解除しないと、せっかく$readmemhでメモリを初期化したのに、またランダム値で初期化されてしまう。これも最初気がつかなくてハマった。

実行結果

上記の設定を施した上でプログラムをコンパイルSRAMに配置、RTLシミュレーションを行った。 Rocket-Chipではシミュレーション結果が表示されるようになっている。

ちょっとわかりにくいが、8220 cycle で最後の計算をしてメモリ領域にストアしている。きちんと実行できていることが確認できた。

しかし、8000サイクル程度かかっているのはちょっとヒドいなあ。

C0:         31 [1] pc=[00010000] W[r10=00000000][1] R[r 0=00000000] R[r20=e4a715cb] inst=[f1402573] csrr    a0, mhartid
C0:         32 [1] pc=[00010004] W[r 5=20000000][1] R[r 0=00000000] R[r 0=00000000] inst=[200002b7] lui     t0, 0x20000
C0:         33 [1] pc=[00010008] W[r 0=0001000c][1] R[r 5=20000000] R[r 0=00000000] inst=[00028067] jr      t0
C0:         34 [0] pc=[00010008] W[r 0=0001000c][0] R[r 5=00000000] R[r 0=e4a715cb] inst=[00028067] jr      t0
C0:         35 [0] pc=[00010008] W[r 0=0001000c][0] R[r 5=00000000] R[r 0=e4a715cb] inst=[00028067] jr      t0
...
C0:       8217 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8218 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8219 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8220 [1] pc=[2000002c] W[r14=00001420][1] R[r14=000013bd] R[r15=00000063] inst=[0000973e] add     a4, a4, a5
C0:       8221 [1] pc=[2000002e] W[r15=00000064][1] R[r15=00000063] R[r 1=00000063] inst=[00000785] addi    a5, a5, 1
C0:       8222 [1] pc=[20000030] W[r 0=00000000][0] R[r15=00000064] R[r11=00000064] inst=[feb796e3] bne     a5, a1, pc - 20
C0:       8223 [1] pc=[20000034] W[r15=20000000][1] R[r 0=00000000] R[r 0=00000000] inst=[200007b7] lui     a5, 0x20000
C0:       8224 [1] pc=[20000038] W[r 0=20000104][0] R[r15=20000000] R[r14=00001420] inst=[10e7a223] sw      a4, 260(a5)
C0:       8225 [1] pc=[2000003c] W[r10=00000000][1] R[r 0=00000000] R[r 0=00001423] inst=[00004501] jr      a0
C0:       8226 [1] pc=[2000003e] W[r 0=20000040][1] R[r 1=00000000] R[r 0=00000000] inst=[00008082] mv      ra, zero