cemu (Cycle level emulator) は RK16 のソフトウェアエミュレータです。tasm でビルドしたバイナリをそのまま流し込んで実行できるので、実機を作る前にプログラムや RTOS の挙動を検証できます。
構成
+------- State -------+ +-------- Hook --------+
| imem: 65536×32bit | | Serial (MMIO I/O) |
| dmem: 65536×16bit | <--> | Intr (外部割込) |
+---------------------+ | Dump (ログ出力) |
+----------------------+
State— CPU 状態。imem (命令メモリ) と dmem (データメモリ + レジスタ) を保持Hook— 命令の実行ごとに呼ばれるプラグイン。MMIO や外部割り込みの注入はここで行う
State
| 項目 | 型 | 内容 |
|---|---|---|
| imem | Vec<u32> | 命令メモリ。65536 word × 32bit |
| dmem | Vec<u16> | データメモリ。65536 word × 16bit。先頭 16 word はレジスタ |
メモリアクセスは state.get(addr) / state.set(addr, val) で行います。レジスタ (PC / RA / FP / T0…) は dmem の先頭 16 word にあるので、全て同じインタフェースから読み書きできます。
実行ループ
loop {
let (addr, code, op) = state.exec(); // 1 命令デコード&実行 (PC を進める)
for hook in hooks.iter_mut() {
state = hook.exec(time, addr, code, state);
}
if state.halt() { break; } // 0x0010 == 1 で停止
}
1 サイクル = 1 命令で、PC が自動的に進みます。HALT (アドレス 0x0010) に 1 が書かれたタイミングでループを抜けます。
割り込み機構
ハードウェアの挙動と一致するように、割り込みは atomic に処理されます。
- 現在 PC を
IRAに退避 csr.ENABLE(bit 0) を 0 に落とす (多重割り込み防止)- PC を割り込みベクタ
0x0004に設定
csr_set_flag(intr_no) で CSR の bit 8 + intr_no に発火フラグが立ち、ベクタから呼ばれる intr_dispatch() がフラグを見て対応するハンドラを呼びます。
Hook プラグイン
pub trait Hook {
fn init(&mut self, cpu: State) -> State;
fn exec(&mut self, time: u64, addr: u16, code: u32, cpu: State) -> State;
}
Hook は State を受け取って State を返す関数で、外界とのやりとり (シリアル送受信、外部割り込み、ダンプ出力) を担います。
Serial
MMIO 0x1000 / 0x1001 を介してシリアルポートをエミュレートします。
0x1000(serial_tx) に書き込まれた値を--soutファイル (or 標準出力) に出力--sinファイルから 1 文字読み、0x1001(serial_rx) で読まれるたびに次の文字を返す- 受信バッファが空のときは
0xFFFFを返す
Intr
設定ファイル (--intr-cfg) で指定したタイミングに外部割り込みを発生させます。タイマ割り込みのシミュレーションや、時分割スケジューラの検証で使います。
Dump
--dump-cfg で指定したアドレス範囲のメモリやレジスタを、各サイクルでログに出します。タスクスイッチの動きや TCB の更新を可視化できます。
起動
cemu \
--sout out/serial.txt \
--intr-cfg intr.yaml \
--dump-cfg dump.yaml \
out/main.bin \
out/const.bin