FPGA開発日記

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

NVDLAの内部構成調査(3. プログラミングシーケンス, Register Description Language)

NVDLAの内部構成調査の続き。前回はNVDLAのBDMAについて調べたが、次は実際にConvolutionの操作を見ていかなければならない。

sanity3のテストパタンを見ると、大量のレジスタを設定してるのだが、それをいちいち調べていくのは大変だ。

まずは、NVDLAの基本的な実行方法について調べていく。

f:id:msyksphinz:20180901013918p:plain
図. NVDLAコアの内部構成 (http://nvdla.org/primer.html より抜粋)

NVDLAのプログラミングシーケンス

以下を参考にした。 Hardware Architectural Specification — NVDLA Documentation

NVDLAサブモジュールをプログラムするための基本的なシーケンスは以下のとおりである。 NVDLAサブモジュールは、すべて同じレジスタを持っており、このシーケンスではCDMAサブモジュールを使ってプログラムを動作させるための手法を説明している。

  1. リセット後、Group0とGroup1はどちらともIDLE状態である。CPUはCDMA_POINTERレジスタを読み出し、PRODUCERレジスタに対してCONSUMERレジスタの値を設定する必要がある(リセット後、CONSUMERレジスタは0に設定されている)。
  2. レジスタグループ0に、1番目のハードウェアレイヤのパラメータを設定する。設定が完了すると、D_OP_ENABLEレジスタenableビットフィールドに1を設定する。
  3. ハードウェアが最初のハードウェアレイヤの処理を始める。
  4. レジスタグループ1がIDLE状態であることを確認するためにS_STATUSレジスタを確認する。
  5. CPUはPRODUCERに1を設定し、グループレイヤ1に対して2番目のハードウェア寧屋のパラメータを設定し始める。これのレジスタに対するプログラムが完了すると、グループ1のD_OP_ENABLEレジスタのenableビットを設定する。
  6. CPUはS_STATUSレジスタを参照して、レジスタグループ0の状態を確認するまだ実行中であれば、CPUは割り込みの発生を待つ。
  7. 現在のハードウェアレイヤについて、ハードウェアが処理を完了する。S_STATUSレジスタ内のアクティブだったグループの値をIDLEに設定し、D_OP_ENABLEレジスタENABLEに設定する。
  8. ハードウェアはCONSUMERフィールドを次のレジスタグループ(今回の場合は1)に設定する。CONSUMERフィールドを進めると、新しいグループ内のEnableビットの位置が決まる。そして次のハードウェアレイヤが即時に開始される。そうでなければ、Enableビットが設定されるまでハードウェアは待ち状態になる。
  9. 実行が完了したハードウェアに対して"done"割り込みが発生する。CPUがdone割り込みの発生をブロックしているならば、上記の処理が継続される。
  10. 必要に応じて上記が繰り返される。

NVDLAのRegister Description Language

NVDLAには大量のレジスタが定義されており、どのレジスタのどのビットフィールドに何が設定できるのかが良く分からない。 一応レジスタ一覧表はあるが、ざっくりと解説してあるだけで具体的な値については説明されていない。

ここで役に立つのが、Register Description Language(=RDL)という気泡で記述されたレジスタの一覧表だ。 このRDLで記述されたレジスタ定義から、VerilogPythonC言語などのシステムレジスタの実装が生成されている。

Register Definition Languageは統一された規格のようだ。以下に仕様が掲載されている。

  • SystemRDL 2.0 Register Description Language

http://www.eda.org/images/downloads/standards/systemrdl/SystemRDL_2.0_Jan2018.pdf

例えば、NVDLAのCDMAで定義されているレジスタを見てみよう。masterブランチ(NVDLA v2)でRDLが定義されている。

  • spec/manual/NVDLA_CDMA.rdl
...
    reg {
        name = "D_MISC_CFG";
        enum D_MISC_CFG_CONV_MODE_enum {
            DIRECT = 1'd0;
            WINOGRAD = 1'd1;
        };
        field {
            encode          = D_MISC_CFG_CONV_MODE_enum;
            sw              = rw;
            hw              = r;
            spec_access     = rw;
            reset           = 0x0;
            reset_mask      = 0x1;
            spec_sw_default = 0x0;
            sw_default_mask = 0x0;
        } CONV_MODE[0:0];
        enum D_MISC_CFG_IN_PRECISION_enum {
            INT8 = 2'd0;
            INT16 = 2'd1;
            FP16 = 2'd2;
        };
        field {
            encode          = D_MISC_CFG_IN_PRECISION_enum;
            sw              = rw;
            hw              = r;
...

これをコンパイルする。NVDLAのルートディレクトリに移って、makeでtree.makeを作成してからの./tools/bin/tmakespec/manualの中身がコンパイルされる。

$ make  # nv_largeを指定する。
$ ./tools/bin/tmake -build vmod
[TMAKE]: building nv_large in spec/defs
[TMAKE]: building nv_large in spec/manual
[TMAKE]: building nv_large in spec/odif
[TMAKE]: building nv_large in vmod/vlibs
[TMAKE]: building nv_large in vmod/include
...
[TMAKE]: building nv_large in vmod/nvdla/retiming
[TMAKE]: building nv_large in vmod/nvdla/top
[TMAKE]: Done nv_large
[TMAKE]: nv_large: PASS

outdir/spec/manualに移動してみる。NVDLA_CDMA.*に関するファイルだけでも以下が生成されていた。

$ ls -1 NVDLA_CDMA*
NVDLA_CDMA.h
NVDLA_CDMA.py
NVDLA_CDMA.rdl
NVDLA_CDMA_reg.sv
NVDLA_CDMA_reg.v
NVDLA_CDMA.vh
NVDLA_CDMA.xml

NVDLA_CDMA_reg_c:
ordt_pio_common.cpp
ordt_pio_common.hpp
ordt_pio.cpp
ordt_pio.hpp

例えば、NVDLA_CDMA.vhには以下のようなレジスタフィールドが定義されている。 S_STATUS_0レジスタはアドレス0x3000に定義されており、以下の2つのフィールドが定義されていることが分かりますね。 (ちなみに同じようなレジスタが2つ生成されているのが、各パイプラインでグループが指定されており、Foreground / Backgroundの指定ができるものと思われる)

  • STATUS_0[1:0] : IDLE=0, RUNNING=1, PENDING=2
  • STATUS_1[17:16] : IDLE=0, RUNNING=0, PENDING=2

  • NVDLA_CDMA.vh

// Register NVDLA_CDMA_S_STATUS_0
#define NVDLA_CDMA_S_STATUS_0                                   32'h3000
#define NVDLA_CDMA_S_STATUS_0_STATUS_0_RANGE                    1:0
#define NVDLA_CDMA_S_STATUS_0_STATUS_0_SIZE                             2
#define NVDLA_CDMA_S_STATUS_0_STATUS_0_IDLE                     2'h0
#define NVDLA_CDMA_S_STATUS_0_STATUS_0_RUNNING                  2'h1
#define NVDLA_CDMA_S_STATUS_0_STATUS_0_PENDING                  2'h2
#define NVDLA_CDMA_S_STATUS_0_STATUS_1_RANGE                    17:16
#define NVDLA_CDMA_S_STATUS_0_STATUS_1_SIZE                             2
#define NVDLA_CDMA_S_STATUS_0_STATUS_1_IDLE                     2'h0
#define NVDLA_CDMA_S_STATUS_0_STATUS_1_RUNNING                  2'h1
#define NVDLA_CDMA_S_STATUS_0_STATUS_1_PENDING                  2'h2