FPGA開発日記

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

TileLinkの仕様まとめ (TL-UL)

TileLinkの3つのレベル

  • TL-UL (TileLink Uncached-Lightweight)
    • 簡易的なTileLink。Get/Putのアクセスのみをサポート。
  • TL-UH (TileLink Uncached-Heavyweight)
    • Uncachedだが、TL-ULよりは高性能。Multibeat operations/atomic accesses, Hint operationをサポート。
  • TL-C (TileLink Cached)
    • Cacheラインの転送をサポート。Channel-B/C/Eをサポート。
TL-UL TL-UH TL-C
Cache line transfers
Channels B+C+E
Multibeat operations
Atomic accesses
Hint operations
Get/Put accesses

TileLinkの5つのチャネル

TileLinkは以下の5つのチャネルを持っている。

  • Channel-A : Master → Slave。 特定アドレスへのリクエスト送信。
  • Channel-B : Slave → Master。 特定アドレスへのキャッシュブロック送信
  • Channel-C : Master → Slave。 キャッシュブロックからのレスポンス
  • Channel-D : Slave → Master。 特定アドレスからのレスポンス
  • Channel-E : Master → Slave。 キャッシュブロック転送での最終ハンドシェーク。

各チャネルでの信号説明の際に登場するTypeについてここで紹介しておく。

Type Direction Description
X クロック入力・リセット信号 TileLink Agentへの入力信号
C 制御信号 転送制御中には変化しない制御信号群
D データ信号 転送毎に変化する
F Final信号 最終転送の時のみ変化する信号
V Valid信号 C/D/Fに有効な信号が含まれているときのみ駆動する
R Ready信号 V信号が受け入れられた時に駆動する信号

TileLink Uncached Lightweight (TL-UL)

TileLinkの最小構成。以下の2つのオペレーションしか実行することが出来ない。

  • Get操作 : メモリからデータを読み込む。
  • Put操作 : メモリに対してデータを書き込む。書込み操作については、バイト単位での細粒度書き込みが許される。

  • Channel-A での転送できるメッセージ (()内は実際のOpcode)

    • Get(4)
    • PutFullData(0)
    • PutPartialData(1)
  • Channel-D で転送できるメッセージ (()内は実際のOpcode)
    • AccessAckData(1)
    • AccessAck(0)

Get操作 (Read)

  • Master : Getコマンドを発行する
  • Slave : Getコマンドを受け取ると、AccessAckDataを返す。

PutFullData操作 / PullPartialData操作 (Write)

  • Master : PutFullData / PullPartialData を発行する
  • Slave : PutFullData / PullPartialDataコマンドを受け取ると、AccessAckを返す。
f:id:msyksphinz:20180215231604p:plain
図. TL-ULのトランザクションの例。Get/Putのどちらでも、A-Channelでのリクエストに対して、D-ChannelでのAcknowledgeを返す必要がある。

関連記事

SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (4. TileLinkの解析とコンフィグレーション設定)

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

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

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

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

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

関連記事

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)
f:id:msyksphinz:20180214010449p:plain
図. SimpleRAMモジュールに対してWriteコマンドが発行され、データ0xdeadbeefが伝えられた波形。

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 はそもそもどういう意味があるのか。
f:id:msyksphinz:20180215001207p:plain
図. TileLink において連続Read/Writeを実行した様子。上側がA-Channel, 下側がD-Channel

CARRV 2018: Computer Architecture Research with RISC-V

f:id:msyksphinz:20170628010131p:plain

Second Workshop on Computer Architecture Research with RISC-V (CARRV 2018) | carrv.github.io

2018年の研究者向けRISC-V Workshopのウェブサイトが公開されました。今年はISCAとの共催らしい。

CARRVはRISC-V Workshopとは違い、より研究者向けのワークショップとなっています。

RISC-Vを使ったコンピュータアーキテクチャ半導体の研究テーマなど、興味のある方は投稿してみてはどうでしょう?

関連記事

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への参照が入っているようだが、その辺りでコケていないだろうか?慎重にデバッグする必要がありそうだ。

SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (2. バスのマップと新しいモジュールの追加)

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

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

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

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

関連記事

Freedmo-SoC の「Instruction & Data Cache」の領域に向けてメモリのフェッチを発行する

前回紹介したとおり、Freedom SoC E300 Platform のメモリマップには "Instruction & Data RAM" という項目があるため、ここにメモリアクセスを発行すると外部からデータをフェッチしてキャッシュに配置してくれるのだろう、と思い、 0x8000_0000に向けてメモリ読み込み (実際には0x8000_0000に向けてプログラムをジャンプさせる)コードを書いてシミュレーションしてみたが、途中でフェッチが止まってしまいうまく動かなかった。

そこでダンプデータを使ってアクセスの行方を探っていたのだが、

  • ICache からメモリフェッチが Rocket CoreのIFに向けて発行される (0x8000_0000)
  • Rocket CoreのIFからRocketTileに向けてリクエストが発行される (0x8000_0000)
  • Peripheral-Busを通じて、RocketTileの逆方向のバスを通じてリクエストがRocket-Coreに戻ってくる。
  • DCache/ICache にリクエストが入って消えた。

というわけで、"Instruction & Data RAM"という場所にアクセスしても意味がないらしい。

f:id:msyksphinz:20180211021741p:plain
図. リクエストの解析の様子。Rocket-Coreから出てきたリクエストは、Rocket-Tileの外へ出ていき、そして戻って来て、ICache/DCacheのアクセスとなっていた

というか、Freedom E300のマニュアル的には、外部からこれらのキャッシュにアクセスするためのメモリアドレスであって、Rocket-Coreがメモリアクセスを行うためには、0x8000_0000ではなくて、普通にE31 Coreplex のメモリマップの場所を使わなければならない。

f:id:msyksphinz:20180209230017p:plain
表. Freedom E300 Platform のメモリマップ
f:id:msyksphinz:20180209230407p:plain
表. Coreplex E31 のメモリマップ

つまり、Coreplex E31のマニュアルにのっとると、システムバスとかペリフェラルバスからのアクセスを発生させるためには、0x2000_0000 もしくは 0x4000_0000 へアクセスを行わなければならない。

で、0x2000_0000にアクセスするとPeripheral-Bus なのでとりあえず外部に出るのだが、そこにはFreedom E300 PlatformではQSPIにつながっており、QSPIのプロトコルに変換されて外に出ていくわけだ。

実現したいことは、Freedom E300 Platformに適当にSRAMを接続してそこから命令とデータをフェッチできる環境を作りたい。 とりあえず、QSPIを取り除いて、そこにROMでもRAMでも接続してみたい。

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

Freedom E300 Platformの改造

Scalaについてはよくわからないのだが、QSPIの接続されている0x2000_0000のアドレスマップに張り付いているモジュールを取り除き、MaskROMを0x2000_0000に接続する。 なぜRAMではなくROMなのかというと、その場で接続できるRAMモジュールが見当たらなかったためだ(笑)。

diff --git a/src/main/scala/everywhere/e300artydevkit/Config.scala b/src/main/scala/everywhere/e300artydevkit/Config.scala
index a85d1e6..3dd9f90 100644
--- a/src/main/scala/everywhere/e300artydevkit/Config.scala
+++ b/src/main/scala/everywhere/e300artydevkit/Config.scala
@@ -35,11 +35,11 @@ class E300DevKitPeripherals extends Config((site, here, up) => {
   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 PeripherySPIFlashKey => List(
+  //   SPIFlashParams(
+  //     fAddress = 0x20000000,
+  //     rAddress = 0x10014000,
+  //     sampleDelay = 3))
   case PeripheryUARTKey => List(
     UARTParams(address = 0x10013000),
     UARTParams(address = 0x10023000))
@@ -48,7 +48,9 @@ class E300DevKitPeripherals extends Config((site, here, up) => {
   case PeripheryMockAONKey =>
     MockAONParams(address = 0x10000000)
   case PeripheryMaskROMKey => List(
-    MaskROMParams(address = 0x10000, name = "BootROM"))
+    MaskROMParams(address = 0x10000,    name = "BootROM"),
+    MaskROMParams(address = 0x20000000, name = "TestROM")
+  )
 })

 // Freedom E300 Arty Dev Kit Peripherals
diff --git a/src/main/scala/everywhere/e300artydevkit/FPGAChip.scala b/src/main/scala/everywhere/e300artydevkit/FPGAChip.scala
index e0b0634..ea3edb8 100644
--- a/src/main/scala/everywhere/e300artydevkit/FPGAChip.scala
+++ b/src/main/scala/everywhere/e300artydevkit/FPGAChip.scala
@@ -44,13 +44,13 @@ class E300ArtyDevKitFPGAChip(implicit override val p: Parameters) extends ArtySh
     // SPI flash IOBUFs
     //---------------------------------------------------------------------

-    IOBUF(qspi_sck, dut.io.pins.qspi.sck)
-    IOBUF(qspi_cs,  dut.io.pins.qspi.cs(0))
-
-    IOBUF(qspi_dq(0), dut.io.pins.qspi.dq(0))
-    IOBUF(qspi_dq(1), dut.io.pins.qspi.dq(1))
-    IOBUF(qspi_dq(2), dut.io.pins.qspi.dq(2))
-    IOBUF(qspi_dq(3), dut.io.pins.qspi.dq(3))
+    // IOBUF(qspi_sck, dut.io.pins.qspi.sck)
+    // IOBUF(qspi_cs,  dut.io.pins.qspi.cs(0))
+    //
+    // IOBUF(qspi_dq(0), dut.io.pins.qspi.dq(0))
+    // IOBUF(qspi_dq(1), dut.io.pins.qspi.dq(1))
+    // IOBUF(qspi_dq(2), dut.io.pins.qspi.dq(2))
+    // IOBUF(qspi_dq(3), dut.io.pins.qspi.dq(3))

     //---------------------------------------------------------------------
     // JTAG IOBUFs

diff --git a/src/main/scala/everywhere/e300artydevkit/Platform.scala b/src/main/scala/everywhere/e300artydevkit/Platform.scala
index be1789a..289f97e 100644
--- a/src/main/scala/everywhere/e300artydevkit/Platform.scala
+++ b/src/main/scala/everywhere/e300artydevkit/Platform.scala
@@ -39,7 +39,7 @@ class E300ArtyDevKitPlatformIO(implicit val p: Parameters) extends Bundle {
   val pins = new Bundle {
     val jtag = new JTAGPins(() => PinGen(), false)
     val gpio = new GPIOPins(() => PinGen(), p(PeripheryGPIOKey)(0))
-    val qspi = new SPIPins(() => PinGen(), p(PeripherySPIFlashKey)(0))
+    /* val qspi = new SPIPins(() => PinGen(), p(PeripherySPIFlashKey)(0)) */
     val aon = new MockAONWrapperPins()
   }
   val jtag_reset = Bool(INPUT)
@@ -159,8 +159,8 @@ class E300ArtyDevKitPlatform(implicit val p: Parameters) extends Module {
   // Result of Pin Mux
   GPIOPinsFromPort(io.pins.gpio, sys.gpio(0))

-  // Dedicated SPI Pads
-  SPIPinsFromPort(io.pins.qspi, sys.qspi(0), clock = sys.clock, reset = sys.reset, syncStages = 3)
+  // // Dedicated SPI Pads
+  // SPIPinsFromPort(io.pins.qspi, sys.qspi(0), clock = sys.clock, reset = sys.reset, syncStages = 3)

   // JTAG Debug Interface
   val sjtag = sys.debug.systemjtag.get
diff --git a/src/main/scala/everywhere/e300artydevkit/System.scala b/src/main/scala/everywhere/e300artydevkit/System.scala
index c3fbd96..8ef51e9 100644
--- a/src/main/scala/everywhere/e300artydevkit/System.scala
+++ b/src/main/scala/everywhere/e300artydevkit/System.scala
@@ -26,7 +26,7 @@ class E300ArtyDevKitSystem(implicit p: Parameters) extends RocketCoreplex
     with HasPeripheryDebug
     with HasPeripheryMockAON
     with HasPeripheryUART
-    with HasPeripherySPIFlash
+    /* with HasPeripherySPIFlash */
     with HasPeripherySPI
     with HasPeripheryGPIO
     with HasPeripheryPWM
@@ -40,7 +40,7 @@ class E300ArtyDevKitSystemModule[+L <: E300ArtyDevKitSystem](_outer: L)
     with HasPeripheryUARTModuleImp
     with HasPeripherySPIModuleImp
     with HasPeripheryGPIOModuleImp
-    with HasPeripherySPIFlashModuleImp
+    /* with HasPeripherySPIFlashModuleImp */
     with HasPeripheryMockAONModuleImp
     with HasPeripheryPWMModuleImp
     with HasPeripheryI2CModuleImp {

これで再生成を行う。

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

これで、MaskROMが2種類作られる。ただし、とりあえず初期値は1つしか指定できないようだ。

make -f Makefile.e300artydevkit romgen
make -C /home/msyksphinz/work/freedom/bootrom/xip romgen
make[1]: ディレクトリ '/home/msyksphinz/work/freedom/bootrom/xip' に入ります
/home/msyksphinz/work/freedom/rocket-chip/scripts/vlsi_rom_gen /home/msyksphinz/work/freedom/builds/e300artydevkit/sifive.freedom.everywhere.e300artydevkit.E300ArtyDevKitConfig.rom.conf /home/msyksphinz/work/freedom/builds/e300artydevkit/xip.hex > /home/msyksphinz/work/freedom/builds/e300artydevkit/rom.v
/home/msyksphinz/work/freedom/rocket-chip/scripts/vlsi_rom_gen:131: UserWarning: vlsi_rom_gen detected multiple ROMs. ROM contents will be duplicated.
  warnings.warn('vlsi_rom_gen detected multiple ROMs. ROM contents will be duplicated.')
...

このVerilogファイルを使って再度RTLシミュレーションを行ってみた。 まだROMの状態ではあるが、TestROMに対して正常なメモリアクセスを発行させることができたようだ。 次は、テスト用のRAMを作って、接続作業を行ってみる。

f:id:msyksphinz:20180211025016p:plain

調べ物をしたりまとめたりする場合には Vivaldi ウェブブラウザが超絶便利だと言う話

ブラウザといえば、Internet Explorer(Edge?)か、Chromeである。

しかし、Chromeはどんどん遅くなっていき不便、ほかにいろいろ無いかなと探した結果、今はVivaldiに落ち着いている。

Vivaldi Community – Free webmail, blogging platform and tech forum.

このVivaldiChromeには無いいろんな便利な機能がある。特に、ブラウザで資料を読みながらブログにまとめ上げる私のようなスタイルでは、「資料とノートを同時に表示する」という要望がある。この要望に対して、Vivaldiはピッタリなのだ。

そもそもWebパネルが便利

これはVivaldiユーザなら全員使っていると思う。ブラウザの再度の領域に別の情報を表示することができる。

これはおそらくモバイル表示モードに設定されているので、

  • Twitterのアカウントなどを表示させておき、常時表示させておく。
  • Inboxのメール一覧を常に表示させておく

などの活用方法がある。

f:id:msyksphinz:20180210184716p:plain

タブのタイリング

例えばネットで英語の資料を読んでいるとき、その資料を読みながら翻訳したり、メモを取っていく必要がある。 メモを手書きで書けばよいのだが、やはり後に残すためにMarkdownなりノートにとっておきたい。 このとき、ブラウザとノートを行ったり来たりするのはあまりにも不便だ。

また、自分はメモを共有したりどこでも読めるようにするために、Jupyter Notebookを使ってウェブ上にメモを残すようにしている。 つまり、やりたいことはJupyterのノート画面と、今読んでいる資料のページを同時に表示したいのだ。

このときに超絶便利なのがVivaldiの「タブのタイリング」という機能だ。

f:id:msyksphinz:20180210184916p:plain

2つのタブを選択して、「タブのタイリング」をクリックすると、

f:id:msyksphinz:20180210185151p:plain

2つのタブを並んで表示させることができる。

これにより、左側のタブに読みたい文章、右側のタブでJupyter Notebookを表示させてまとめを記入していく、ということが可能になる。

2枚だけのタイリングだけではない

このタイリング、2枚だけでなく、それ以上のタブもまとめてタイリングすることができる。

f:id:msyksphinz:20180210185357p:plain

このタイリングのスタイルについては、右下のページの並べ方で制御することができる。

f:id:msyksphinz:20180210185442p:plain

4枚縦にタブを並べた例。あまり便利じゃないけど。

f:id:msyksphinz:20180210185513p:plain

という訳で、まるで勉強の時のスタイル。

  • 読みたい内容
  • まとめるためのノート

を同時にタイリングして表示できる点が非常に重宝している。

それ以外にも、2つのウェブページを比較したり、いろんな活用法がある。どんどん活用していきたい。

SiFiveのFreedom E300 Platformを単体でカスタマイズできるようにする (1. 全体構造からブートROMへのリクエストまでの仕組み)

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

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

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

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

Freedom SoCはRocket-Coreを含むRISC-VのSoCプラットフォームだが、主にコアとなるTile部分と、外部のペリフェラル部分から構成されている。

f:id:msyksphinz:20180209234009p:plain

Freedom E300 システムのソースファイル全体像

Freedom E300システムは、Rocket-Chipと、外部のSoC部に分かれて構成されている。

freedom
src/main/
└── scala
    ├── everywhere
    │   └── e300artydevkit
    └── unleashed
        └── u500vc707devkit
rocket-chip/src/
└── main
    └── scala
        ├── amba
        │   ├── ahb
        │   ├── apb
        │   └── axi4
        ├── config
        ├── coreplex
        ├── devices
        │   ├── debug
        │   └── tilelink
        ├── diplomacy
        ├── groundtest
        ├── interrupts
        ├── jtag
        ├── regmapper
        ├── rocket
        ├── system
        ├── tile
        ├── tilelink
        ├── unittest
        └── util

Freedom E300 Platform のメモリマップ

Freedom E300 Platform は、以下のメモリマップを持っている。 このなかで、最初の0x0000_0000から0x0FFF_FFFFまでは、E3 Coreplex Manualを参照すること、と書いてある通り、こちらに掲載しているのはあくまでもSoCのメモリマップだ。 さらにSoC内部のCoreplex IPについては、E3 Coreplex Manualを参照する必要がある。

f:id:msyksphinz:20180209230017p:plain

f:id:msyksphinz:20180209230427p:plain

といっても、実際にはリセット後にBootROMに命令フェッチが入っており、そちらからジャンプする仕組みなっている。 BootROMには、以下のプログラムがコンパイルされて格納されている。

  .section .text.init
  .option norvc
  .globl _start
_start:
  csrr a0, mhartid
  la a1, dtb
  li t0, XIP_TARGET_ADDR
  jr t0

  .section .rodata
dtb:
  .incbin DEVICE_TREE

XIP_TARGET_ADDRは、以下のコンパイルコマンドで決定されている。

$(elf): xip.S $(dtb)
    $(CC) $(CFLAGS) -DXIP_TARGET_ADDR=0x20400000 -DDEVICE_TREE='"$(dtb)"' $(LFLAGS) -o $@ $<

TARGET_ADDRを見てわかるとおり、まずは0x2040_0000に配置されているQSPIへ命令をフェッチするようになっている。 つまりBootROMのターゲットアドレスを書き換えれば、所望の場所からプログラムをロードしてフェッチできそうな気がしている。

最初にブートするMaskROMは、System.scalaでmaskROMとして定義されている。

  • freedom/src/main/scala/everywhere/e300artydevkit/System.scala
class E300ArtyDevKitSystemModule[+L <: E300ArtyDevKitSystem](_outer: L)
  extends RocketCoreplexModule(_outer)
    with HasPeripheryDebugModuleImp
    with HasPeripheryUARTModuleImp
...
  • freedom/rocket-chip/src/main/scala/devices/tilelink/MaskROM.scala
trait HasPeripheryMaskROMSlave extends HasPeripheryBus {
  val maskROMParams = p(PeripheryMaskROMKey)
  val maskROMs = maskROMParams map { params =>
    val maskROM = LazyModule(new TLMaskROM(params))
    maskROM.node := pbus.toFixedWidthSingleBeatSlave(maskROM.beatBytes)
    maskROM
  }
}

RTLシミュレーション

一応E300 Freedom SoCのVerilogファイルだけ抜き出して適当にテストベンチをつなげ、ClockとResetをつなげてシミュレーションをしてみると、 とりあえずMaskROMへのフェッチが発生しているところまでは確認できる。

BootROMから、QSPIの領域(0x2040_0000)へジャンプしているため、そこからQSPIへのアクセスに移っているようだ。これをDRAMの領域に差し替えて、外部のRAMから命令をフェッチできるようにしてみよう。

f:id:msyksphinz:20180209235015p:plain

関連記事