FPGA開発日記

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

Rocket-ChipのTLBがメモリの属性を取得する仕組み (Rocket-ChipでPMAのエラー発生源を追う)

Rocket-ChipのTLBの解析の続き。Rocket-ChipのPMAはTileLinkによって接続された各デバイスのアドレス情報を引っ張ってきてテーブルを作り、許可されるアドレス領域かどうかを判断していることが分かった。

具体的なテストケースを使って確認しよう。rv64mi-p-accessテストパタンをRocket-Chip(Chipyard)上で実行して確認してみよう。

./simulator-chipyard-RocketConfig-debug +verbose -v rv64mi-p-accses.vcd /home/msyksphinz/work/riscv/chipyard/riscv-tools-install/riscv64-unknown-elf/share/riscv-tests/isa/rv64mi-p-access 2>&1 | spike-dasm | tee rv64mi-p-accses.log

この時にテストケースは非常に大きなアドレスにジャンプしようとしてTLBエラーが発生している。 このエラーが発生する仕組みを追いかける。

 C0:        424 [1] pc=[0000000080000194] W[r 5=ffffffffffffffff][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[fff0029b] addiw   t0, zero, -1
 C0:        425 [1] pc=[0000000080000198] W[r 5=8000000000000000][1] R[r 5=ffffffffffffffff] R[r 0=0000000000000000] inst=[03f29293] slli    t0, t0, 63
 C0:        426 [1] pc=[000000008000019c] W[r 5=80000000800001dc][1] R[r 5=8000000000000000] R[r 7=00000000800001dc] inst=[0072c2b3] xor     t0, t0, t2
 C0:        427 [1] pc=[00000000800001a0] W[r 3=0000000000000002][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00200193] li      gp, 2
 C0:        428 [1] pc=[00000000800001a4] W[r 6=0000000000000001][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00100313] li      t1, 1
 C0:        429 [1] pc=[00000000800001a8] W[r 9=00000000800001a8][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000497] auipc   s1, 0x0
 C0:        430 [1] pc=[00000000800001ac] W[r 9=00000000800001b8][1] R[r 9=00000000800001a8] R[r 0=0000000000000000] inst=[01048493] addi    s1, s1, 16
 C0:        431 [1] pc=[00000000800001b0] W[r 7=0000000000000000][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000393] li      t2, 0
 C0:        432 [1] pc=[00000000800001b4] W[r 7=00000000800001b8][1] R[r 5=80000000800001dc] R[r 0=0000000000000000] inst=[000283e7] jalr    t2, t0, 0
// ここでアドレス0x80000000800001dcにジャンプする。非常に大きなアドレスでアクセス許可範囲外
 C0:        436 [0] pc=[ffffff80800001dc] W[r 8=0000000000000000][0] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000000] c.addi4spn s0, sp, 0
 C0:        441 [1] pc=[0000000080000004] W[r30=0000000000000001][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[34202f73] csrr    t5, mcause
 C0:        442 [1] pc=[0000000080000008] W[r31=0000000000000008][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00800f93] li      t6, 8
 C0:        444 [1] pc=[000000008000000c] W[r 0=0000000000000000][0] R[r30=0000000000000001] R[r31=0000000000000008] inst=[03ff0a63] beq     t5, t6, pc + 52

波形を確認すると、この時にTLBのinst.ae信号が落ちている。これにより命令フェッチアクセスエラーが発生していることが明らかになった。

f:id:msyksphinz:20210913002015p:plain

このinst.ae信号はテーブルのhit信号と、さらにpx_array信号のAND演算によって成り立っている。

   io.resp.ae.inst := (~px_array & hits).orR

px_array信号は、テーブル12を参照している時にはprot_xを参照する。このprot_x信号は以下のような感じでfastCheck()という関数で作成されている。まあこれがTileLinkのデバイスチェックに繋がっている。

   val pr_array = Cat(Fill(nPhysicalEntries, prot_r), normal_entries.map(_.pr).asUInt) & ~ptw_ae_array
   val pw_array = Cat(Fill(nPhysicalEntries, prot_w), normal_entries.map(_.pw).asUInt) & ~ptw_ae_array
   val px_array = Cat(Fill(nPhysicalEntries, prot_x), normal_entries.map(_.px).asUInt) & ~ptw_ae_array
   def fastCheck(member: TLManagerParameters => Boolean) =
     legal_address && edge.manager.fastProperty(mpu_physaddr, member, (b:Boolean) => Bool(b))

   val prot_x = fastCheck(_.executable) && !deny_access_to_debug && pmp.io.x

legal_addressというのは、各デバイスが当該アクセスを許可できるかどうかを探索していることを意味する。mpu_phyaddrが当該物理アドレス

   val legal_address = edge.manager.findSafe(mpu_physaddr).reduce(_||_)

具体的に波形を観察すると、これまではlegal_address_T_49(おそらくメモリ領域だと思う)までのアクセスが許可されていたが、物理アドレス0x8080001DCとなりその領域を外れ、どのデバイスのアドレス領域でも無くなってしまった。これによりlegal_addressがすべて0となり、命令フェッチのアクセスエラーへとつながるようだ。

f:id:msyksphinz:20210913002549p:plain