FPGA開発日記

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

Rocket Chipの足回りを理解する (7. デバッグモジュールによるデータの転送)

前回の続き。Rocket Chipがどのようにしてプログラムをロードするのかを引き続き解析している。riscv-fesvrのdtm.ccを見てその動作を解析している。

write_chunk (uint64_t taddr, size_t len, const void* src)

ターゲットのアドレスtaddrからlenバイト分、srcデータを書き込む。 情報として処理しなければならないのはこの3つだが、これをどのようにして処理するのか。

f:id:msyksphinz:20171004014749p:plain

まずはターゲットアドレスtaddrレジスタs0に転送する。転送するためには転送コマンドを送信する必要があるが、これをどのようにして作っているのかというと、

  • riscv-fesvr/fesvr/dtm.cc
  uint32_t command = AC_ACCESS_REGISTER_TRANSFER |
    AC_ACCESS_REGISTER_WRITE |
    AC_AR_SIZE(xlen) |
    AC_AR_REGNO(S0);

  RUN_AC_OR_DIE(command, prog, 3, data, xlen/(4*8));

RUN_AC_OR_DIEというのはrun_abstract_command()という関数のためのWrapperである。 Abstract Commandというのは何かというとDebug仕様書に書いてあるのだが、Abstract Commandのフォーマットに従って制御される。 Abstract Commandのフォーマットは以下。

f:id:msyksphinz:20171004003513p:plain

  • Access Register : Rocket Coreのレジスタにアクセスする。Write=0, Transfer=1でデータをコアから取得し、Write=1, Transfer=1でデータをコアに転送する。
  • Postexecが設定されている場合、プログラムバッファを実行する。

プログラムバッファ

このPostExecで設定されるプログラムバッファに書き込まれたプログラムを、Rocket Chipは実行することが出来るのだが、この実行できるプログラムはRISC-Vのプログラムそのままに他ならない。 dtm.ccにはdefineデスクリプションにより簡単なプログラムが定義されているが、

  • riscv-fesvr/fesvr/dtm.cc
#define LOAD(xlen, dst, base, imm) \
  (((xlen) == 64 ? 0x00003003 : 0x00002003) \
   | ((dst) << 7) | ((base) << 15) | (uint32_t)ENCODE_ITYPE_IMM(imm))
#define STORE(xlen, src, base, imm) \
  (((xlen) == 64 ? 0x00003023 : 0x00002023) \
   | ((src) << 20) | ((base) << 15) | (uint32_t)ENCODE_STYPE_IMM(imm))
#define JUMP(there, here) (0x6f | (uint32_t)ENCODE_UJTYPE_IMM((there) - (here)))
#define BNE(r1, r2, there, here) (0x1063 | ((r1) << 15) | ((r2) << 20) | (uint32_t)ENCODE_SBTYPE_IMM((there) - (here)))
#define ADDI(dst, src, imm) (0x13 | ((dst) << 7) | ((src) << 15) | (uint32_t)ENCODE_ITYPE_IMM(imm))
#define SRL(dst, src, sh) (0x5033 | ((dst) << 7) | ((src) << 15) | ((sh) << 20))
#define FENCE_I 0x100f
#define EBREAK  0x00100073

このプログラムは、Abstract Commandのプログラムサイズ設定フィールドが1以上に設定されている場合、EBREAKで停止する。

  • read_chunk()
  prog[0] = LOAD(xlen, S1, S0, 0);
  prog[1] = ADDI(S0, S0, xlen/8);
  prog[2] = EBREAK;
  • write_chunk()
  prog[0] = STORE(xlen, S1, S0, 0);
  prog[1] = ADDI(S0, S0, xlen/8);
  prog[2] = EBREAK;

プログラムをデバッグレジスタにロードしては、Rocket Chipを動かしてプログラムをストアするという方式でメモリに書き込みを行っていることが分かる。