第17章
S-modeの実装 (1. CSRの実装)
本章ではSupervisorモード(S-mode)を実装します。S-modeは主にOSのようなシステムアプリケーションを動かすために使用される特権レベルです。S-modeがある環境には必ずU-modeが実装されています。
S-modeを導入することで変わる主要な機能はトラップです。M-mode、U-modeだけの環境ではトラップで特権レベルをM-modeに変更していましたが、M-modeではなくS-modeに遷移できるようになります。これに伴い、トラップ関連のCSR(stvec、sepc、scause、stvalなど)が追加されます。
S-modeで新しく導入される大きな機能として仮想記憶システムがあります。仮想記憶システムはページングを使って仮想的なアドレスを使用できるようにする仕組みです。これについては第18章「S-modeの実装 (2. 仮想記憶システム)」で解説します。
他にはscounterenレジスタ、トラップから戻るためのSRET命令などが追加されます。また、Supervisor software interruptを提供するSSWIデバイスも実装します。それぞれ解説しながら実装します。
eeiパッケージに、本書で実装するS-modeのCSRをすべて定義します。
1: enum CsrAddr: logic<12> { 2: // Supervisor Trap Setup 3: SSTATUS = 12'h100, 4: SIE = 12'h104, 5: STVEC = 12'h105, 6: SCOUNTEREN = 12'h106, 7: // Supervisor Trap Handling 8: SSCRATCH = 12'h140, 9: SEPC = 12'h141, 10: SCAUSE = 12'h142, 11: STVAL = 12'h143, 12: SIP = 12'h144, 13: // Supervisor Protection and Translation 14: SATP = 12'h180,
17.1 misa.Extensions、mstatus.SXL、mstatus.MPPの実装
S-modeを実装しているかどうかはmisa.ExtensionsのSビットで確認できます。
misa.ExtensionsのSビットを1
に設定します(リスト17.2)。
1: let misa : UIntX = {2'd2, 1'b0 repeat XLEN - 28, 26'b00000101000001000100000101}; // U, S, M, I, C, A
S-modeのときのXLENはSXLENと定義されており、mstatus.SXLで確認できます。本書ではSXLENが常に64
になるように実装します。
mstatus.SXLを64
を示す値である2
に設定します(リスト17.3、リスト17.4)。
1: const MSTATUS_UXL: UInt64 = 2 << 32; 2: const MSTATUS_SXL: UInt64 = 2 << 34;
1: always_ff { 2: if_reset { 3: mode = PrivMode::M; 4: mstatus = MSTATUS_SXL | MSTATUS_UXL;
今のところmstatus.MPPにはM-modeとU-modeを示す値しか書き込めないようにしているので、S-modeの値(2'b10
)も書き込めるように変更します(リスト17.5)。これにより、MRET命令でS-modeに移動できるようになります。
1: function validate_mstatus ( 2: mstatus: input UIntX, 3: wdata : input UIntX, 4: ) -> UIntX { 5: var result: UIntX; 6: result = wdata; 7: // MPP 8: if wdata[12:11] == 2'b10 { 9: result[12:11] = mstatus[12:11]; 10: } 11: return result; 12: }
17.2 scounterenレジスタの実装
「16.6 mcounterenレジスタの実装」では、ハードウェアパフォーマンスモニタにU-modeでアクセスできるかをmcounterenレジスタで制御できるようにしました。S-modeを導入するとmcounterenレジスタはS-modeがハードウェアパフォーマンスモニタにアクセスできるかを制御するレジスタに変わります。また、mcounterenレジスタの代わりにU-modeでハードウェアパフォーマンスモニタにアクセスできるかを制御する32ビットのscounterenレジスタが追加されます。
scounterenレジスタのフィールドのビット配置はmcounterenレジスタと同じです。また、U-modeでハードウェアパフォーマンスにアクセスできる条件は、mcounterenレジスタとscounterenレジスタの両方によって許可されている場合になります。
scounterenレジスタを作成し、読み書きできるようにします(リスト17.6、リスト17.7、リスト17.8、リスト17.9、リスト17.10、リスト17.11)。
1: var scounteren: UInt32;
1: mtval = 0; 2: scounteren = 0; 3: led = 0;
1: CsrAddr::MTVAL : mtval, 2: CsrAddr::SCOUNTEREN: {1'b0 repeat XLEN - 32, scounteren}, 3: CsrAddr::LED : led,
1: const SCOUNTEREN_WMASK: UIntX = 'h0000_0000_0000_0007 as UIntX;
1: CsrAddr::MTVAL : MTVAL_WMASK, 2: CsrAddr::SCOUNTEREN: SCOUNTEREN_WMASK, 3: CsrAddr::LED : LED_WMASK,
1: CsrAddr::MTVAL : mtval = wdata; 2: CsrAddr::SCOUNTEREN: scounteren = wdata[31:0]; 3: CsrAddr::LED : led = wdata;
ハードウェアパフォーマンスモニタにアクセスするときに許可を確認する仕組みを実装します(リスト17.12)。S-modeでアクセスするときはmcounterenレジスタだけ確認し、U-modeでアクセスするときはmcounterenレジスタとscounterenレジスタを確認します。
1: let expt_zicntr_priv : logic = is_wsc && (mode <= PrivMode::S && case csr_addr { 2: CsrAddr::CYCLE : !mcounteren[0], 3: CsrAddr::TIME : !mcounteren[1], 4: CsrAddr::INSTRET: !mcounteren[2], 5: default : 0, 6: } || mode <= PrivMode::U && case csr_addr { 7: CsrAddr::CYCLE : !scounteren[0], 8: CsrAddr::TIME : !scounteren[1], 9: CsrAddr::INSTRET: !scounteren[2], 10: default : 0, 11: }); // attempt to access Zicntr CSR without permission
17.3 sstatusレジスタの実装

図17.1: sstatusレジスタ
sstatusレジスタはmstatusレジスタの一部をS-modeで読み込み、書き込みできるようにしたSXLENビットのレジスタです。本章ではmstatusレジスタに読み込み、書き込みマスクを適用することでsstatusレジスタを実装します。
sstatusレジスタの書き込みマスクを定義します(リスト17.13、リスト17.14)。
1: const SSTATUS_WMASK : UIntX = 'h0000_0000_0000_0000 as UIntX;
1: CsrAddr::MTVAL : MTVAL_WMASK, 2: CsrAddr::SSTATUS : SSTATUS_WMASK, 3: CsrAddr::SCOUNTEREN: SCOUNTEREN_WMASK,
読み込みマスクを定義し、mstatusレジスタにマスクを適用した値をsstatusレジスタの値にします(リスト17.15、リスト17.16、リスト17.17)。
1: const SSTATUS_RMASK: UIntX = 'h8000_0003_018f_e762;
1: let sstatus : UIntX = mstatus & SSTATUS_RMASK;
1: CsrAddr::MTVAL : mtval, 2: CsrAddr::SSTATUS : sstatus, 3: CsrAddr::SCOUNTEREN: {1'b0 repeat XLEN - 32, scounteren},
マスクを適用した書き込みを実装します(リスト17.18)。書き込みマスクが適用されたwdataと、書き込みマスクをビット反転した値でマスクされたmstatusレジスタの値のORを書き込みます。
1: CsrAddr::SSTATUS : mstatus = validate_mstatus(mstatus, wdata | mstatus & ~SSTATUS_WMASK);
17.4 トラップの委譲
17.4.1 トラップの委譲
S-modeが実装されているとき、S-modeとU-modeで発生するトラップの遷移先の特権レベルをM-modeからS-modeに変更(委譲)することができます。特権レベルがM-modeのときに発生したトラップの特権レベルの遷移先をS-modeに変更することはできません。
M-modeからS-modeに委譲されたトラップのトラップベクタは、mtvecではなくstvecになります。また、mepcではなくsepcにトラップが発生した命令アドレスを格納し、scauseにトラップの原因を示す値、stvalに例外に固有の情報、sstatus.SPPにトラップ前の特権レベル、sstatus.SPIEにsstatus.SIE、sstatus.SIEに0
を格納します。これ以降、トラップでx-modeに遷移するときに変更、参照するCSRを例えばxtvec、xepc、xcause、xtval、mstatus.xPPのように頭文字をxにして呼ぶことがあります。
例外の委譲
medelegレジスタは、どの例外を委譲するかを制御する64ビットのレジスタです。medelegレジスタの下からi
番目のビットが立っているとき、S-mode、U-modeで発生したcauseがi
の例外をS-modeに委譲します。M-modeで発生した例外はS-modeに委譲されません。
Environment call from M-mode例外のように委譲することができない命令のmedelegレジスタのビットは1
に変更できません。
割り込みの委譲
midelegレジスタは、どの割り込みを委譲するかを制御するMXLENビットのレジスタです。各割り込みはmie、mipレジスタと同じ場所のmidelegレジスタのビットによって委譲されるかどうかが制御されます。
M-mode、S-mode、U-modeが実装されたCPUで、割り込みでM-modeに遷移する条件は次の通りです。
- 割り込み原因に対応したmipレジスタのビットが
1
である - 割り込み原因に対応したmieレジスタのビットが
1
である - 現在の特権レベルがM-mode未満である。またはmstatus.MIEが
1
である - 割り込み原因に対応したmidelegレジスタのビットが
0
である
割り込みでS-modeに遷移する条件は次の通りです。
- 割り込み原因に対応したsipレジスタのビットが
1
である - 割り込み原因に対応したsieレジスタのビットが
1
である - 現在の特権レベルがS-mode未満である。またはS-modeのとき、sstatus.SIEが
1
である
sip、sieレジスタは、それぞれmip、mieレジスタの委譲された割り込みのビットだけ読み込み、書き込みできるようにしたレジスタです。委譲されていない割り込みに対応したビットは読み込み専用の0
になります。S-modeに委譲された割り込みは、特権レベルがM-modeのときは発生しません。
S-modeに委譲された割り込みは外部割り込み、ソフトウェア割り込み、タイマ割り込みの順に優先されます。委譲されていない割り込みを同じタイミングで発生させられるとき、委譲されていない割り込みが優先されます。
本書ではM-modeの外部割り込み(Machine external interrupt)、ソフトウェア割り込み(Machine software interrupt)、タイマ割り込み(Machine timer interrupt)はS-modeに委譲できないように実装します*1。
[*1] 多くの実装ではこれらの割り込みを委譲できないように実装するようです。そのため、本書で実装するコアでも委譲できないように実装します。
17.4.2 トラップに関連するレジスタを作成する
S-modeに委譲されたトラップで使用するstvec、sscratch、sepc、scause、stvalレジスタを作成します(リスト17.19、リスト17.20、リスト17.21、リスト17.22、リスト17.23、リスト17.24)。
1: var stvec : UIntX ; 2: var sscratch : UIntX ; 3: var sepc : UIntX ; 4: var scause : UIntX ; 5: var stval : UIntX ;
1: stvec = 0; 2: sscratch = 0; 3: sepc = 0; 4: scause = 0; 5: stval = 0;
1: CsrAddr::STVEC : stvec, 2: CsrAddr::SSCRATCH : sscratch, 3: CsrAddr::SEPC : sepc, 4: CsrAddr::SCAUSE : scause, 5: CsrAddr::STVAL : stval,
それぞれ、mtvec、mscratch、mepc、mcause、mtvalレジスタと同じ書き込みマスクを設定します。
1: const STVEC_WMASK : UIntX = 'hffff_ffff_ffff_fffd; 2: const SSCRATCH_WMASK : UIntX = 'hffff_ffff_ffff_ffff; 3: const SEPC_WMASK : UIntX = 'hffff_ffff_ffff_fffe; 4: const SCAUSE_WMASK : UIntX = 'hffff_ffff_ffff_ffff; 5: const STVAL_WMASK : UIntX = 'hffff_ffff_ffff_ffff;
1: CsrAddr::STVEC : STVEC_WMASK, 2: CsrAddr::SSCRATCH : SSCRATCH_WMASK, 3: CsrAddr::SEPC : SEPC_WMASK, 4: CsrAddr::SCAUSE : SCAUSE_WMASK, 5: CsrAddr::STVAL : STVAL_WMASK,
1: CsrAddr::STVEC : stvec = wdata; 2: CsrAddr::SSCRATCH : sscratch = wdata; 3: CsrAddr::SEPC : sepc = wdata; 4: CsrAddr::SCAUSE : scause = wdata; 5: CsrAddr::STVAL : stval = wdata;
17.4.3 stvecレジスタの実装
トラップが発生するとき、遷移先の特権レベルがS-modeならstvecレジスタの値にジャンプするようにします(リスト17.25、リスト17.26)。割り込み、例外それぞれにレジスタを選択する変数を定義し、mtvecを使っていたところを新しい変数に置き換えます。
1: let interrupt_xtvec : Addr = if interrupt_mode == PrivMode::M ? mtvec : stvec; 2: let interrupt_vector: Addr = if interrupt_xtvec[0] == 0 ? 3: {interrupt_xtvec[msb:2], 2'b0} 4: : // Direct 5: {interrupt_xtvec[msb:2] + interrupt_cause[msb - 2:0], 2'b0} 6: ; // Vectored
1: let expt_xtvec : Addr = if expt_mode == PrivMode::M ? mtvec : stvec; 2: let expt_vector: Addr = {expt_xtvec[msb:2], 2'b0};
17.4.4 トラップでsepc、scause、stvalレジスタを変更する
トラップが発生するとき、遷移先の特権レベルがS-modeならsepc、scause、stvalレジスタを変更するようにします。
トラップ時にtrap_mode_next
で処理を分岐します(リスト17.27)。
1: if raise_expt || raise_interrupt { 2: let xepc: Addr = if raise_expt ? pc : // exception 3: if raise_interrupt && is_wfi ? pc + 4 : pc; // interrupt when wfi / interrupt 4: if trap_mode_next == PrivMode::M { 5: mepc = xepc; 6: mcause = trap_cause; 7: if raise_expt { 8: mtval = expt_value; 9: } 10: // save mstatus.mie to mstatus.mpie 11: // and set mstatus.mie = 0 12: mstatus[7] = mstatus[3]; 13: mstatus[3] = 0; 14: // save current privilege level to mstatus.mpp 15: mstatus[12:11] = mode; 16: } else { 17: sepc = xepc; 18: scause = trap_cause; 19: if raise_expt { 20: stval = expt_value; 21: } 22: }
17.4.5 mstatusのSIE、SPIE、SPPビットを実装する
mstatusレジスタのSIE、SPIE、SPPビットを実装します。mstatus.SIEはS-modeに委譲された割り込みのグローバル割り込みイネーブルビットです。mstatus.SPIEはS-modeに委譲されたトラップが発生するときにmstatus.SIEを退避するビットです。mstatus.SPPはS-modeに委譲されたトラップが発生するときに、トラップ前の特権レベルを書き込むビットです。S-modeに委譲されたトラップはS-modeかU-modeでしか発生しないため、mstatus.SPPは特権レベルを区別するために十分な1ビット幅のフィールドになっています。
mstatus、sstatusレジスタのSIE、SPIE、SPPビットに書き込めるようにします(リスト17.28、リスト17.29)。
1: const MSTATUS_WMASK : UIntX = 'h0000_0000_0020_19aa as UIntX;
1: const SSTATUS_WMASK : UIntX = 'h0000_0000_0000_0122 as UIntX;
トラップでS-modeに遷移するとき、sstatus.SPIEにsstatus.SIE、sstatus.SIEに0
、sstatus.SPPにトラップ前の特権レベルを格納します(リスト17.30)。
1: } else { 2: sepc = xepc; 3: scause = trap_cause; 4: if raise_expt { 5: stval = expt_value; 6: } 7: // save sstatus.sie to sstatus.spie 8: // and set sstatus.sie = 0 9: mstatus[5] = mstatus[1]; 10: mstatus[1] = 0; 11: // save current privilege mode (S or U) to sstatus.spp 12: mstatus[8] = mode[0]; 13: }
17.4.6 SRET命令を実装する
SRET命令の実装
SRET命令は、S-modeのCSR(sepc、sstatusなど)を利用してトラップ処理から戻るための命令です。SRET命令はS-mode以上の特権レベルのときにしか実行できません。
inst_decoderモジュールでSRET命令をデコードできるようにします(リスト17.31)。
1: OP_SYSTEM: f3 != 3'b000 && f3 != 3'b100 || // CSRR(W|S|C)[I] 2: bits == 32'h00000073 || // ECALL 3: bits == 32'h00100073 || // EBREAK 4: bits == 32'h30200073 || //MRET 5: bits == 32'h10200073 || //SRET 6: bits == 32'h10500073, // WFI
SRET命令を判定し、ジャンプ先と遷移先の特権レベルを命令によって切り替えます(リスト17.32、リスト17.33、リスト17.34)。
1: let is_sret: logic = inst_bits == 32'h10200073;
1: assign trap_return = valid && (is_mret || is_sret) && !raise_expt && !raise_interrupt; 2: let trap_return_mode : PrivMode = if is_mret ? mstatus_mpp : mstatus_spp; 3: let trap_return_vector: Addr = if is_mret ? mepc : sepc;
1: assign trap_vector = switch { 2: raise_expt : expt_vector, 3: raise_interrupt: interrupt_vector, 4: trap_return : trap_return_vector, 5: default : 0, 6: };
SRET命令を実行するとき、sstatus.SIEにsstatus.SPIE、sstatus.SPIEに0
、sstatus.SPPに実装がサポートする最小の特権レベル(U-mode)を示す値を格納します(リスト17.35)。
1: } else if trap_return { 2: if is_mret { 3: // set mstatus.mie = mstatus.mpie 4: // mstatus.mpie = 0 5: mstatus[3] = mstatus[7]; 6: mstatus[7] = 0; 7: // set mstatus.mpp = U (least privilege level) 8: mstatus[12:11] = PrivMode::U; 9: } else if is_sret { 10: // set sstatus.sie = sstatus.spie 11: // sstatus.spie = 0 12: mstatus[1] = mstatus[5]; 13: mstatus[5] = 0; 14: // set sstatus.spp = U (least privilege level) 15: mstatus[8] = 0; 16: } 17: }
SRET命令をS-mode未満の特権レベルで実行しようとしたら例外が発生するようにします(リスト17.36)。
1: let expt_trap_return_priv: logic = (is_mret && mode <: PrivMode::M) || (is_sret && mode <: PrivMode::S);
mstatus.TSRの実装
mstatusレジスタのTSR(Trap SRET)ビットは、SRET命令をS-modeで実行したときに例外を発生させるかを制御するビットです。1
のとき、Illegal instruction例外が発生するようになります。
mstatus.TSRを変更できるようにします(リスト17.37)。
1: const MSTATUS_WMASK : UIntX = 'h0000_0000_0060_19aa as UIntX;
1: let mstatus_tsr : logic = mstatus[22];
1: let expt_trap_return_priv: logic = (is_mret && mode <: PrivMode::M) || (is_sret && (mode <: PrivMode::S || (mode == PrivMode::S && mstatus_tsr)));
17.4.7 SEI、SSI、STIを実装する
S-modeを導入すると、S-modeの外部割り込み(Supervisor external interrupt)、ソフトウェア割り込み(Supervisor software interrupt)、タイマ割り込み(Supervisor timer interrupt)に対応するmip、mieレジスタのビットを変更できるようになります。
例外、割り込みはそれぞれmedeleg、midelegレジスタでS-modeに処理を委譲することができます。委譲された割り込みのmipレジスタの値はsipレジスタで観測できるようになり、割り込みを有効にするかをsieレジスタで制御できるようになります。
mip、mieレジスタの変更
mipレジスタのSEIP、SSIP、STIPビット、mieレジスタのSEIE、SSIE、STIEビットを変更できるようにします。
書き込みマスクを変更、実装します(リスト17.40、リスト17.41)。
1: const MIP_WMASK : UIntX = 'h0000_0000_0000_0222 as UIntX; 2: const MIE_WMASK : UIntX = 'h0000_0000_0000_02aa as UIntX;
1: CsrAddr::MIP : MIP_WMASK,
mip_reg
レジスタを作成します。mip
の値を、mip_reg
とACLINTの状態をOR演算したものに変更します(リスト17.42)。
1: var mip_reg: UIntX; 2: let mip : UIntX = mip_reg | {
mip_reg
レジスタのリセット、書き込みを実装します(リスト17.43、リスト17.44)。wdata
にはACLINTの状態が含まれているので、書き込みマスクをもう一度適用します。
1: mie = 0; 2: mip_reg = 0; 3: mcounteren = 0;
1: CsrAddr::MTVEC : mtvec = wdata; 2: CsrAddr::MIP : mip_reg = wdata & MIP_WMASK; 3: CsrAddr::MIE : mie = wdata;
causeの設定
S-modeの割り込みのcauseを設定します(リスト17.45)。
1: let interrupt_cause : UIntX = switch { 2: interrupt_pending[3]: CsrCause::MACHINE_SOFTWARE_INTERRUPT, 3: interrupt_pending[7]: CsrCause::MACHINE_TIMER_INTERRUPT, 4: interrupt_pending[9]: CsrCause::SUPERVISOR_EXTERNAL_INTERRUPT, 5: interrupt_pending[1]: CsrCause::SUPERVISOR_SOFTWARE_INTERRUPT, 6: interrupt_pending[5]: CsrCause::SUPERVISOR_TIMER_INTERRUPT, 7: default : 0, 8: };
medeleg、mideleg、sip、sieレジスタの実装

図17.2: sipレジスタ

図17.3: sieレジスタ
medeleg、mideleg、sip、sieレジスタを実装します。
medeleg、midelegレジスタはそれぞれ委譲できる例外、割り込みに対応するビットだけ書き換えられるようにします。sipレジスタはmidelegレジスタで委譲された割り込みに対応するビットだけ値を参照できるように、sieレジスタはmidelegレジスタで委譲された割り込みに対応するビットだけ書き換えられるようにします。
レジスタを作成し、読み込めるようにします(リスト17.46、リスト17.47、リスト17.48、リスト17.49、リスト17.50、リスト17.51)。
1: var medeleg : UInt64; 2: var mideleg : UIntX ;
1: let sip : UIntX = mip & mideleg; 2: var sie : UIntX ;
1: medeleg = 0; 2: mideleg = 0;
1: sie = 0;
1: CsrAddr::MEDELEG : medeleg, 2: CsrAddr::MIDELEG : mideleg,
1: CsrAddr::SIP : sip, 2: CsrAddr::SIE : sie & mideleg,
書き込みマスクを設定し、書き込めるようにします(リスト17.52、リスト17.53、リスト17.54、リスト17.55、リスト17.56、リスト17.57)。
1: const MEDELEG_WMASK : UIntX = 'hffff_ffff_fffe_f7ff; 2: const MIDELEG_WMASK : UIntX = 'h0000_0000_0000_0222 as UIntX;
1: const SIE_WMASK : UIntX = 'h0000_0000_0000_0222 as UIntX;
1: CsrAddr::MEDELEG : MEDELEG_WMASK, 2: CsrAddr::MIDELEG : MIDELEG_WMASK,
1: CsrAddr::SIE : SIE_WMASK & mideleg,
1: CsrAddr::MEDELEG : medeleg = wdata; 2: CsrAddr::MIDELEG : mideleg = wdata;
1: CsrAddr::SIE : sie = wdata;
17.4.8 割り込み条件、トラップの動作を変更する
作成したCSRを利用して、割り込みが発生する条件、トラップが発生したときのCSRの操作を変更します。
例外が発生するとき、遷移先の特権レベルをmedelegレジスタによって変更します(リスト17.58)。
1: let expt_mode : PrivMode = if mode == PrivMode::M || !medeleg[expt_cause[5:0]] ? PrivMode::M : PrivMode::S;
割り込みの発生条件と参照するCSRを、遷移先の特権レベルごとに用意します(リスト17.59、リスト17.60)。
1: // Interrupt to M-mode 2: let interrupt_pending_mmode: UIntX = mip & mie & ~mideleg; 3: let raise_interrupt_mmode : logic = (mode != PrivMode::M || mstatus_mie) && interrupt_pending_mmode != 0; 4: let interrupt_cause_mmode : UIntX = switch { 5: interrupt_pending_mmode[3]: CsrCause::MACHINE_SOFTWARE_INTERRUPT, 6: interrupt_pending_mmode[7]: CsrCause::MACHINE_TIMER_INTERRUPT, 7: interrupt_pending_mmode[9]: CsrCause::SUPERVISOR_EXTERNAL_INTERRUPT, 8: interrupt_pending_mmode[1]: CsrCause::SUPERVISOR_SOFTWARE_INTERRUPT, 9: interrupt_pending_mmode[5]: CsrCause::SUPERVISOR_TIMER_INTERRUPT, 10: default : 0, 11: };
1: // Interrupt to S-mode 2: let interrupt_pending_smode: UIntX = sip & sie; 3: let raise_interrupt_smode : logic = (mode <: PrivMode::S || (mode == PrivMode::S && mstatus_sie)) && interrupt_pending_smode != 0; 4: let interrupt_cause_smode : UIntX = switch { 5: interrupt_pending_smode[9]: CsrCause::SUPERVISOR_EXTERNAL_INTERRUPT, 6: interrupt_pending_smode[1]: CsrCause::SUPERVISOR_SOFTWARE_INTERRUPT, 7: interrupt_pending_smode[5]: CsrCause::SUPERVISOR_TIMER_INTERRUPT, 8: default : 0, 9: };
M-mode向けの割り込みを優先して利用します(リスト17.61)。
1: // Interrupt 2: let raise_interrupt : logic = valid && can_intr && (raise_interrupt_mmode || raise_interrupt_smode); 3: let interrupt_cause : UIntX = if raise_interrupt_mmode ? interrupt_cause_mmode : interrupt_cause_smode; 4: let interrupt_xtvec : Addr = if interrupt_mode == PrivMode::M ? mtvec : stvec; 5: let interrupt_vector: Addr = if interrupt_xtvec[0] == 0 ? 6: {interrupt_xtvec[msb:2], 2'b0} 7: : // Direct 8: {interrupt_xtvec[msb:2] + interrupt_cause[msb - 2:0], 2'b0} 9: ; // Vectored 10: let interrupt_mode: PrivMode = if raise_interrupt_mmode ? PrivMode::M : PrivMode::S;
17.5 ソフトウェア割り込みの実装 (SSWI)
SSWIデバイスはソフトウェア割り込み(Supervisor software insterrupt)を提供するためのデバイスです。SSWIデバイスにはハードウェアスレッド毎に4バイトのSETSSIPレジスタが用意されています(表17.1)SETSSIPレジスタを読み込むと常に0
を返しますが、最下位ビットに1
を書き込むとそれに対応するハードウェアスレッドのmip.SSIPビットが1
になります。
表17.1: SSWIデバイスのメモリマップ
オフセット | レジスタ |
---|---|
0000 | SETSSIP0 |
0004 | SETSSIP1 |
.. | .. |
3ff8 | SETSSIP4094 |
3ffc | MTIME |

図17.4: setssipレジスタ
今のところmhartidが0
のハードウェアスレッドしか存在しないため、SETSSIP0のみ実装します。aclint_ifインターフェースに、mipレジスタのSSIPビットを1
にする要求のためのsetssip
を作成します(リスト17.62)。
1: interface aclint_if { 2: var msip : logic ; 3: var mtip : logic ; 4: var mtime : UInt64; 5: var setssip: logic ; 6: modport master { 7: msip : output, 8: mtip : output, 9: mtime : output, 10: setssip: output, 11: }
aclintモジュールでSETSSIP0への書き込みを検知し、最下位ビットをsetssip
に接続します(リスト17.63)。
1: always_comb { 2: aclint.setssip = 0; 3: if membus.valid && membus.wen && membus.addr == MMAP_ACLINT_SETSSIP { 4: aclint.setssip = membus.wdata[0]; 5: } 6: }
csrunitモジュールでsetssip
を確認し、mip.SSIPを立てるようにします(リスト17.64、リスト17.65、リスト17.66)。
1: let setssip: UIntX = {1'b0 repeat XLEN - 2, aclint.setssip, 1'b0};
1: } else { 2: mcycle += 1; 3: mip_reg |= setssip;
1: CsrAddr::MIP : mip_reg = (wdata & MIP_WMASK) | setssip;