RISC-Vの実装であるRocket-Chipは、RISC-Vの最新使用に追従しているため最初に見るべきデザインとしてはよくできているが、
- Chiselで記述されており(初心者には)可読性が低い。
- 外部のSoCプラットフォームについて情報がない
ことから、なかなかオリジナルのSoCをくみ上げるためのデザインとしてポーティングしにくい。
とはいえ、TileLinkは単なるバスのはずだし、クロックとリセットさえ突っ込めばとりあえずフェッチリクエストが上がってくるはずだ。 そのあたりを解析して、Rocket-Chipを利用したオリジナルSoCプラットフォーム構築のための情報収集を行っていこう。
関連記事
- SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (1. 全体構造からブートROMへのリクエストまでの仕組み)
- SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (2. バスのマップと新しいモジュールの追加)
- SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (3. SRAMモジュールを作成して接続、テストパタンで動作を確認する)
- SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (4. TileLinkの解析とコンフィグレーション設定)
TileLink のコンフィグレーション設定
やっと気が付いたのだが、Freedom SoC (しいてはRocket-Core)を生成するときにメモリマップが表示されるのだが、これをよく見てみると ターゲットとしている0x2000_0000 にはReadの許可しか入っていなかった。
Generated Address Map 0 - 1000 ARWX debug-controller@0 10000 - 12000 R XC rom@10000 2000000 - 2010000 ARW clint@2000000 c000000 - 10000000 ARW interrupt-controller@c000000 10000000 - 10001000 ARW aon@10000000 10012000 - 10013000 ARW gpio@10012000 10013000 - 10014000 ARW serial@10013000 10015000 - 10016000 ARW pwm@10015000 10016000 - 10017000 ARW i2c@10016000 10023000 - 10024000 ARW serial@10023000 10024000 - 10025000 ARW spi@10024000 10025000 - 10026000 ARW pwm@10025000 10034000 - 10035000 ARW spi@10034000 10035000 - 10036000 ARW pwm@10035000 20000000 - 20002000 R ram@20000000 <-- ここにRWしたいのだが、Readのフラグしか立っていない。 80000000 - 80004000 ARWX dtim@80000000
これをどうやって立てるのかずっと調査していたのだが、どうやら各モジュールのインタフェースとなっているTileLinkのコンフィグレーションで、Read/Write/Executableの権限を制御できるようになっているらしい。
こんなの、資料が無いと分からんわ...
diff --git a/src/main/scala/devices/tilelink/SimpleRAM.scala b/src/main/scala/devices/tilelink/SimpleRAM.scala index d7c5d74..5b7552e 100644 --- a/src/main/scala/devices/tilelink/SimpleRAM.scala +++ b/src/main/scala/devices/tilelink/SimpleRAM.scala @@ -32,6 +32,8 @@ class TLSimpleRAM(c: SimpleRAMParams)(implicit p: Parameters) extends LazyModule regionType = RegionType.UNCACHED, executable = true, supportsGet = TransferSizes(1, beatBytes), + supportsPutPartial = TransferSizes(1, beatBytes), + supportsPutFull = TransferSizes(1, beatBytes), fifoId = Some(0))), // requests are handled in order beatBytes = beatBytes))) @@ -71,4 +73,3 @@ class TLSimpleRAM(c: SimpleRAMParams)(implicit p: Parameters) extends LazyModule in.e.ready := Bool(true) } }
というわけで、TileLink側でアクセス許可を渡してやると、プログラムからRead/Writeできるようになった。
生成されるメモリマップ表は以下のように変わり、0x2000_0000 - 0x20002000にRead/Write/Executableが与えられるようになった (CはCachableか?Uncachedの構成にしてほしいのだが...)
Generated Address Map 0 - 1000 ARWX debug-controller@0 10000 - 12000 R XC rom@10000 2000000 - 2010000 ARW clint@2000000 c000000 - 10000000 ARW interrupt-controller@c000000 10000000 - 10001000 ARW aon@10000000 10012000 - 10013000 ARW gpio@10012000 10013000 - 10014000 ARW serial@10013000 10015000 - 10016000 ARW pwm@10015000 10016000 - 10017000 ARW i2c@10016000 10023000 - 10024000 ARW serial@10023000 10024000 - 10025000 ARW spi@10024000 10025000 - 10026000 ARW pwm@10025000 10034000 - 10035000 ARW spi@10034000 10035000 - 10036000 ARW pwm@10035000 20000000 - 20002000 ARWXC ram@20000000 80000000 - 80004000 ARWX dtim@80000000
しかし、まだ疑問点がある。1回目のWriteだけで止まってしまったぞ? TileLinkはAcknowledgeを出す必要があるのか?解析は続く...
li a0, 0x20000000 lw t0, 0x20(a0) nop nop nop nop nop nop nop nop li t0, 0xdeadbeef sw t0, 0x00(a0) sw t0, 0x04(a0) sw t0, 0x08(a0)
2018/02/15追記。結局、TileLinkのアービトレーションが必要らしい。 in.d.bits
および in.d.bits.opcode
を制御するような論理を追加した。これらは、SRAM.scala
を参考にした。
この辺、資料がちゃんと用意されていないと、分かりようがないよ...
// Flow control - when (in.d.fire()) { d_full := Bool(false) } - when (in.a.fire() && read) { d_full := Bool(true) } + when (in.d.fire()) { d_full := Bool(false) } + when (in.a.fire()) { d_full := Bool(true) } in.d.valid := d_full in.a.ready := in.d.ready || !d_full + in.d.bits := edge.AccessAck(d_source, d_size, !d_legal) + // avoid data-bus Mux + in.d.bits.data := d_data + in.d.bits.opcode := Mux(d_read, TLMessages.AccessAckData, TLMessages.AccessAck) + + val read = in.a.bits.opcode === TLMessages.Get + val rdata = Wire(Vec(beatBytes, Bits(width = 8))) + val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
Dチャネルに正しくAcknowledge を挟むことによりTileLinkが動作するようになった。ただし、まだ不明な点がある。
d.bits.error
が立ち上がっているがこれは問題無いのか。d.bits.source
はそもそもどういう意味があるのか。