概要
前回 ビルドしたLinuxを用いてシミュレーションを行った。 しかし、例外が発生し、処理が進まない。 今回は、原因を追求してみようと思う。
現状
まずは、現状確認がてら実行した様子を示す。

status: 00000100 badaddr: dead4ead cause: 0000000c epc: c044da24
がCSRの情報である。
cause: 0xc
なので、Instruction page faultである様子。
どんな命令があるのか見てみる。\
riscv32-unknown-linux-gnu-objdump linux/vmlinux |less
でディスアセンブルする。
c044da24:
で検索すると以下の命令であることがわかる。
__printk_safe_flush
関数の命令の一部である。
シミュレータで0ef725af amoswap.w.aqrl a1, a5, (a4)
が実行される際のレジスタをダンプする。
a4: c140c9c4 a5: 00000000
であることがわかる。
よって、a1 <- *(0xc140c9c4), *(0xc140c9c4) <- 0x00000000
となる。
また、シーケンシャルコンシステンシーで、ワードサイズのオペレーションである。
というか、Instruction page faultのはずが、Store amo address misalignedとなっている。
おかしい。
- おそらく、Store amo address misalignedが正しい
- Store amo address misalignedのときのscauceがおかしい?
- mcauseは?
- OpenSBIがM-ModeのトラップをS-Modeへredirectする。
- Address misalignedの検出回路がおかしい?
- inst: 0x0ef725af
- a1: 0xc140c9c4
- byteen: MEM_WORD
Store amo address misalignedのときのscauceがおかしい?
まずは、mcauseの値を見てみる。
シミュレータ上では、Store amo address misalignedとなっているので、mcauseは正しいはず。
sbi_trap_handler
内(80003b5c)で、csr s1, mcause命令でmcauseをs1レジスタに読み出している。
sbi_trap_handler
はOpenSBIの一部であり、mtvecアドレスがにセットされている。
よって、トラップ時に実行される。S-Modeで対処可能なトラップはredirectされる。
mcauseは0x6となっているのがわかる。
0x6はStore AMO address misalignedを示す。
sbi_trap_redirect
関数の一部で、scauseに0x6をセットしているので、Linuxカーネルには正しく0x6が渡されている。
よって、カーネル内どこかでscauseに0xcがセットされているよう?
ひとまず、これは保留。
Address misalignedの検出回路がおかしい?
検出はアドレス計算をするexecuteステージで行っている。 以下にNSLのコード片を示すが、AMO命令に関してはワード境界に整列されたアドレスに対するワード単位の演算しかサポートしていない。
...
#define MEM_WORD 0x2
#define MEM_HALFWORD 0x1
#define MEM_BYTE 0x0
...
any {
/* Data address misaligned */
((alu_q[1:0] != 2'b00) && ({1'b0, DEREG.funct3[1:0]} == MEM_WORD)): m_misaligned();
((alu_q[0] != 1'b0) && ({1'b0, DEREG.funct3[1:0]} == MEM_HALFWORD)): m_misaligned();
((DEREG.rs1_data[1:0] != 2'b00) && ({1'b0, DEREG.funct3[1:0]} == MEM_WORD)):a_misaligned();
}
/* Exceptions in execute stage */
any {
(DEREG.illegal_instruction): illegal_instruction_execute_stage();
(i_misaligned): instruction_address_misaligned(target_address);
(DEREG.load && m_misaligned): load_address_misaligned(alu_q);
(DEREG.store && m_misaligned): store_address_misaligned(alu_q);
(DEREG.amo && a_misaligned): store_address_misaligned(DEREG.rs1_data);
}
0x0ef725af amoswap.w.aqrl a1, a5, (a4)
より、funct3は0x2である。
rs1_data: 0xc140c9c4
なので、ワード境界に整列している。
また、funct3==0x2なので、MEM_WORDと同値である。
もしかして、rs1_dataが正しくフォワーディングされていないのでは?
rs1_dataが正しくフォワーディングされていないのでは?
フォワーディングが正しくない。 DEREG(Decode/Executeのパイプラインレジスタ)からrs1のデータを読み出しているが、正しくはパイプラインレジスタからではなく、フォワーディングする必要がある。 細かいが、execute_alu_aがexecuteステージに向けたrs1用のフォワーディングパスである。 rs1_dataとして、execute_alu_aを用いる。 フォワーディングするかしないかは、命令ごとにalu_a_forward_en端子にて制御する。 AMO命令では、rs1のデータをフォワーディングをする。 以下のように修正して、再度シミュレータをコンパイルした。
any {
/* Data address misaligned */
((alu_q[1:0] != 2'b00) && ({1'b0, DEREG.funct3[1:0]} == MEM_WORD)): m_misaligned();
((alu_q[0] != 1'b0) && ({1'b0, DEREG.funct3[1:0]} == MEM_HALFWORD)): m_misaligned();
((execute_alu_a[1:0] != 2'b00) && ({1'b0, DEREG.funct3[1:0]} == MEM_WORD)):a_misaligned();
}
/* Exceptions in execute stage */
any {
(DEREG.illegal_instruction): illegal_instruction_execute_stage();
(i_misaligned): instruction_address_misaligned(target_address);
(DEREG.load && m_misaligned): load_address_misaligned(alu_q);
(DEREG.store && m_misaligned): store_address_misaligned(alu_q);
(DEREG.amo && a_misaligned): store_address_misaligned(execute_alu_a);
}
再度、riscv-testsを実施した。正しくパスしている。
というか、前までも(すり抜けて)パスしていた。
問題となっているパターンは
addi a4, s6, -4
amoswap.w.aqrl a1, a5, (a4)
のような先発のrdが後発のrs1となっているパターンである。 そこそこ、限定的な上に、AMO命令なので、気づくのにかなり時間を要した。
Linuxの再実行
再度、Linuxをシミュレータ上で実行してみる。
進展した。しかし、ハングした。
さらなる問題が発生したので、次回はこの問題の原因の追求をする。
仮想メモリのマップ(Virtual kernel memory layout)はどうやって決めているのだろう。
RV32XSoCにはPCIとかインストールしていないよ?