TinyFPGA BX で、AXI4 クロスバ付きの VexRiscv SoC を動かしてみた

(更新

Running VexRiscv SoC with AXI4 crossbar on TinyFPGA BX.

前回まで、VexRiscv プロジェクトで公開されている Murax という SoC で遊んで来ました。Murax は非常に小型の SoC 実装例(デモ)となっていて、小さな論理規模で実装できるのがポイントです。しかしながら、私は今後、この SoC に CPU 以外のバスマスタ(DMA)を追加する実験をしたいと思っており、この Murax が持っているバス MuraxMasterArbiter では、追加のバスマスタを追加するのは難しそうです。

AXI4 クロスバ付き SoC「Briey」

そこで、今回より新たに Briey というもう一つ別の SoC 実装例をベースに評価を進めていきたいと思います。Briey のブロック図を引用させて頂きます。(引用元: https://github.com/SpinalHDL/VexRiscv#briey-soc

Briey は、Axi4CrossbarFactory というライブラリを使って AXI4 のクロスバスイッチを実装しているのが特徴です。このスイッチを使うと複数のバスマスタを接続できそうです。(詳細は勉強中)

TinyFPGA BX で動かす

そこで、VexRiscv のデモに含まれる Briey を TinyFPGA BX で動くようにしてみました。GitHub リポジトリはこちらです。

バス構成だけ Briey を踏襲し、

  • CPU 構成は Murax と同等にする(論理サイズを小さく保つため)
  • ペリフェラルから SdramCtrl、gpioB、VgaCtrl を取り除く

ようにしました。

いくつか注意点と修正点

今回の作業でいくつか落とし穴にはまり苦労しましたので、備忘録を兼ねてまとめておきます。

シフト演算命令が動作しなくなる

Murax の  cpuPlugins 設定を注意深くコピーしていったのですが、一つだけ、LightShifterPlugin を追加し忘れるというポカをしました。これを忘れると、シフト命令(SLL や SLLI など)が正しく動作しなくなります。

本来であれば、LightShifterPlugin か FullBarrelShifterPlugin を忘れていたら警告を出して欲しいところですが、おそらく、DecoderSimplePlugin に catchIllegalInstruction = true を入れていれば、こういう問題は避けれらる、ということでしょう。

UART が動かなくなる

実は最初、私は UART が動いていないのではなく、SpiSlave の割込が動いていないのではないかと(間違って)推測してしまいました。人間、何か先入観があると本来の問題に気づかなくなるものです。

デバッグしていたとき、ふと、FPGA 側 UART の RxD に FTDI チップの TxD を繋いでみると、なんと「SpiSlave の割込が届いているように見える」ではないですか。最初に分かったのは、FPGA 側の RxD をプルアップしてあげると、問題が消えるのです。確かに、RxD をプルアップしないと入力がずっと low になってしまうのですが、なぜ UART の RxD が「SpiSlave の割込に影響を与えるんだ!?」と、まる一日悩んでいました。

実は、、、SpiSlave に影響を与えているのではなくて、RxD が low のままだと、SpinalHDL の UART ライブラリが、RS-232 で使われる break 信号を検出してしまい、UART の機能が「送信も含めて」停止してしまう、というのが本当のところだったのです。ギャフンです。SpinalHDL の UART コードを読んでいて、ようやく気づいた次第です。

という訳で、2つの対策を入れることにしました。一つはもちろん、RxD ピンを内部プルアップにする、というものです。RxD が low だと break というのは理解できますが、RxD を結線しない使い方だってあるからです。私がでっち上げた Briey_iCE40_tinyfpga_bx クラスでは、以下のようなコードを追加しています。

import spinal.lib.blackbox.lattice.ice40.SB_IO

object Briey_iCE40_tinyfpga_bx{
  // 略

  case class Briey_iCE40_tinyfpga_bx(nClkDiv: Int = 0) extends Component{
    val io = new Bundle {
      // 略
      val uart_rxd = in(Analog(Bool))  // ← ここに注意
    }

    // 略

    class SB_IO_PULLUP(pinType: String) extends SB_IO(pinType) {
      addGeneric("PULLUP", 1)
    }
    val rxdPullup = new SB_IO_PULLUP(pinType = "000001")
    rxdPullup.PACKAGE_PIN := io.uart_rxd
    briey.io.uart.rxd := rxdPullup.D_IN_0

    // 略
  }
}

もう一つの対策は、C で書かれたデモコードのほうです。SpinalHDL の UART では、レジスタマップに break 信号の検出を読み出せるビットがあるので、以下のようにして、LED 点滅を早めて警告してみるようにしました。

typedef struct
{
    volatile uint32_t DATA;
    volatile uint32_t STATUS;
    volatile uint32_t CLOCK_DIVIDER;
    volatile uint32_t FRAME_CONFIG;
    volatile uint32_t ERROR;  // ← 追加
} Uart_Reg;

void main()
{
    // 略

    while (1) {
        //略

        // Check UART Rx break
        if (UART->ERROR & (1 << 8))
            GPIO_A->OUTPUT ^= 0x80; // Toggle led 7
    }
}

これでようやく、AXI4 クロスバスイッチの勉強に移れそうです、、、が、クロスバスイッチの実装は難しそうです。どうなることやら。

今日はここまで。

お問い合わせはお気軽に!

お問い合わせを頂いた後、継続して営業活動をしたり、ニュースレター等をお送りしたりすることはございません。
御返答は 24時間以内(営業時間中)とさせて頂いております。必ず返信致しますが、時々アドレス誤りと思われる返信エラーがございます。返答が届かない場合、大変お手数ではございますが別のメールアドレスで督促頂けますと幸いです。