NVDLAの内部構成調査の続き。前回はNVDLAのBDMAについて調べたが、次は実際にConvolutionの操作を見ていかなければならない。
sanity3のテストパタンを見ると、大量のレジスタを設定してるのだが、それをいちいち調べていくのは大変だ。
まずは、NVDLAの基本的な実行方法について調べていく。
NVDLAのプログラミングシーケンス
以下を参考にした。 Hardware Architectural Specification — NVDLA Documentation
NVDLAサブモジュールをプログラムするための基本的なシーケンスは以下のとおりである。 NVDLAサブモジュールは、すべて同じレジスタを持っており、このシーケンスではCDMAサブモジュールを使ってプログラムを動作させるための手法を説明している。
- リセット後、Group0とGroup1はどちらともIDLE状態である。CPUは
CDMA_POINTERレジスタを読み出し、PRODUCERレジスタに対してCONSUMERレジスタの値を設定する必要がある(リセット後、CONSUMERレジスタは0に設定されている)。 - レジスタグループ0に、1番目のハードウェアレイヤのパラメータを設定する。設定が完了すると、
D_OP_ENABLEレジスタのenableビットフィールドに1を設定する。 - ハードウェアが最初のハードウェアレイヤの処理を始める。
- レジスタグループ1がIDLE状態であることを確認するために
S_STATUSレジスタを確認する。 - CPUは
PRODUCERに1を設定し、グループレイヤ1に対して2番目のハードウェア寧屋のパラメータを設定し始める。これのレジスタに対するプログラムが完了すると、グループ1のD_OP_ENABLEレジスタのenableビットを設定する。 - CPUは
S_STATUSレジスタを参照して、レジスタグループ0の状態を確認するまだ実行中であれば、CPUは割り込みの発生を待つ。 - 現在のハードウェアレイヤについて、ハードウェアが処理を完了する。
S_STATUSレジスタ内のアクティブだったグループの値をIDLEに設定し、D_OP_ENABLEレジスタをENABLEに設定する。 - ハードウェアは
CONSUMERフィールドを次のレジスタグループ(今回の場合は1)に設定する。CONSUMERフィールドを進めると、新しいグループ内のEnableビットの位置が決まる。そして次のハードウェアレイヤが即時に開始される。そうでなければ、Enableビットが設定されるまでハードウェアは待ち状態になる。 - 実行が完了したハードウェアに対して"done"割り込みが発生する。CPUがdone割り込みの発生をブロックしているならば、上記の処理が継続される。
- 必要に応じて上記が繰り返される。
NVDLAのRegister Description Language
NVDLAには大量のレジスタが定義されており、どのレジスタのどのビットフィールドに何が設定できるのかが良く分からない。 一応レジスタ一覧表はあるが、ざっくりと解説してあるだけで具体的な値については説明されていない。
ここで役に立つのが、Register Description Language(=RDL)という気泡で記述されたレジスタの一覧表だ。 このRDLで記述されたレジスタ定義から、Verilog、Python、C言語などのシステムレジスタの実装が生成されている。
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/tmakeでspec/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