FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (3. SRAMモジュールを作成して接続、テストパタンで動作を確認する)

RISC-Vの実装であるRocket-Chipは、RISC-Vの最新使用に追従しているため最初に見るべきデザインとしてはよくできているが、

  • Chiselで記述されており(初心者には)可読性が低い。
  • 外部のSoCプラットフォームについて情報がない

ことから、なかなかオリジナルのSoCをくみ上げるためのデザインとしてポーティングしにくい。

とはいえ、TileLinkは単なるバスのはずだし、クロックとリセットさえ突っ込めばとりあえずフェッチリクエストが上がってくるはずだ。 そのあたりを解析して、Rocket-Chipを利用したオリジナルSoCプラットフォーム構築のための情報収集を行っていこう。

f:id:msyksphinz:20180211025506p:plain
図. とりあえず実現したいRISC-V SoCブロック。まずはQSPIを取り除いて、だれでもアクセスできるTestRAMを接続したい。

関連記事

Freedmo-SoC に独自のRAMモジュールを配置する

Rocket-Chipには、P-Busに接続できるモジュールとしてはマスクROMやPLICなどが定義されている。 この中にTestRAMというモジュールがあるが、これはシミュレーション用で、論理合成はできない仕組みだ。

rocket-chip/src/main/scala/devices/tilelink$ tree
.
├── BootROM.scala
├── BusBlocker.scala
├── BusBypass.scala
├── Clint.scala
├── Error.scala
├── MaskROM.scala
├── Plic.scala
├── TestRAM.scala
└── Zero.scala
  • rocket-chip/src/main/scala/devices/tilelink/TestRAM.scala
// Do not use this for synthesis! Only for simulation.
class TLTestRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4, errors: Seq[AddressSet] = Nil)(implicit p: Parameters) extends LazyModule

そこで、SimpleなRAMを作成して接続してみる。

  • rocket-chip/src/main/scala/devices/tilelink/SimpleRAM.scala
class TLSimpleRAM(c: SimpleRAMParams)(implicit p: Parameters) extends LazyModule {
  val beatBytes = c.width/8
  val node = TLManagerNode(Seq(TLManagerPortParameters(
    Seq(TLManagerParameters(
      address            = AddressSet.misaligned(c.address, c.depth*beatBytes),
      resources          = new SimpleDevice("ram", Seq("msyksphinz,simpleRAM")).reg("mem"),
...
  lazy val module = new LazyModuleImp(this) {
    val memory = Mem(1024, UInt(width = c.width))

    val (in, edge) = node.in(0)
    val mem_address = edge.addr_hi(in.a.bits.address - UInt(c.address))(log2Ceil(c.depth)-1, 0)

    val read = (in.a.bits.opcode === TLMessages.Get)
    when (in.a.fire() && !read) {
      memory(mem_address) := in.a.bits.data
      // memory(mem_address) := 0.U
    }
...

これをP-Busのマップ上に定義して、FIRRTLでVerilogファイルを生成する。 - freedom/src/main/scala/everywhere/e300artydevkit/Config.scala

// Freedom E300 Arty Dev Kit Peripherals
class E300DevKitPeripherals extends Config((site, here, up) => {
  case PeripheryGPIOKey => List(
    GPIOParams(address = 0x10012000, width = 32, includeIOF = true))
  case PeripheryPWMKey => List(
    PWMParams(address = 0x10015000, cmpWidth = 8),
    PWMParams(address = 0x10025000, cmpWidth = 16),
    PWMParams(address = 0x10035000, cmpWidth = 16))
  case PeripherySPIKey => List(
    SPIParams(csWidth = 4, rAddress = 0x10024000, sampleDelay = 3),
    SPIParams(csWidth = 1, rAddress = 0x10034000, sampleDelay = 3))
  // case PeripherySPIFlashKey => List(
  //   SPIFlashParams(
  //     fAddress = 0x20000000,
  //     rAddress = 0x10014000,
  //     sampleDelay = 3))
  case PeripheryUARTKey => List(
    UARTParams(address = 0x10013000),
    UARTParams(address = 0x10023000))
  case PeripheryI2CKey => List(
    I2CParams(address = 0x10016000))
  case PeripheryMockAONKey =>
    MockAONParams(address = 0x10000000)
  case PeripheryMaskROMKey => List(
    MaskROMParams(address = 0x10000,    name = "BootROM")
  )
  case PeripherySimpleRAMKey => List(
    SimpleRAMParams(address = 0x20000000, name = "TestRAM")
  )
})

以下のように入力して、Verilogファイルを生成する。

make -f Makefile.e300artydevkit clean && make -f Makefile.e300artydevkit verilog && make -f Makefile.e300artydevkit romgen

VerilogシミュレーションでSimpleRAMへのアクセスを確認する

次に、アセンブリコードを書いてSimpleSRAMモジュールへアクセスが入ることを確認しよう。 このCoreplex E31のメモリマップでは、Uncached / Cachedをどう使い分ければよいのかわからないが、デフォルトでは必ずMissになるはずなのでReadは上手くできるはずだ。

    .section .text.init
    .option norvc
    .globl _start
_start:
        csrr a0, mhartid

    li  a0, 0x20000000
    li  a1, 0xdeadbeef
        lw      t0, 0x00(a0)
        lw      t1, 0x04(a0)
        lw      t2, 0x08(a0)
        lw      t3, 0x0c(a0)
        lw      t4, 0x10(a0)
        lw      t5, 0x14(a0)
        lw      t6, 0x18(a0)
        lw      a1, 0x1c(a0)

        la a1, dtb
        li t0, XIP_TARGET_ADDR
        jr t0

    .section .rodata
dtb:
    .incbin DEVICE_TREE

波形を確認すると、以下のようにリクエストがSimpleSRAMに到達しているのが確認できた。

f:id:msyksphinz:20180212195639p:plain
図. SimpleSRAMのアドレスへRead Requestを発行して、データの応答が帰っている様子が確認できた。

しかし、Store命令を発行しても、SimpleSRAMモジュールにリクエストが到達しない。考えられるのは、 - キャッシュに入ったままFlushされない - そもそも発行する命令とアドレスが間違っている

だが、どちらも納得できない。DCacheにリクエストが入った後TLBへの参照が入っているようだが、その辺りでコケていないだろうか?慎重にデバッグする必要がありそうだ。