assembly-riscv について
assembly-riscvは、RISC-Vアセンブリや関連コードの作成・デバッグを行うエージェント向けのスキルです。RV32/RV64命令、呼び出し規約、ISA拡張、インラインアセンブリ、QEMUシミュレーションとGDBデバッグを解説します。
主な機能
- 01RV32/RV64命令とレジスター
- 02psABI呼び出し規約
- 03ISA拡張の命名
- 04GCC/Clangのインラインアセンブリ
- 05QEMUシミュレーションとGDBデバッグ
活用シーン
- 01RISC-Vアセンブリルーチンの作成
- 02psABIに沿った引数の受け渡し
- 03ターゲットに合わせたISA拡張の選択
- 04RISC-Vプログラムのシミュレーションやデバッグ
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill assembly-riscvインストール後、Claude Code で/assembly-riscvを入力して読み込まれたか確認します。
同梱ファイルreferences/riscv-abi.md
詳細
RISC-V アセンブリ
目的
RISC-V のアセンブリプログラミングについて、エージェントをガイドします。RV32/RV64 命令セット、レジスター名と呼び出し規約(psABI)、ISA 拡張の命名規則、GCC/Clang でのインラインアセンブリ、圧縮(RVC)命令、GDB リモートデバッグを使った QEMU ベースのシミュレーションを扱います。
トリガー
- 「RISC-V アセンブリの書き方を教えてください」
- 「RISC-V の呼び出し規約で使うレジスターは何ですか?」
- 「C で RISC-V のインライン asm を使うにはどうすればよいですか?」
- 「RISC-V の拡張文字(IMAFD)は何を意味しますか?」
- 「QEMU で RISC-V をシミュレートするにはどうすればよいですか?」
- 「GDB で RISC-V コードをデバッグするにはどうすればよいですか?」
ワークフロー
1. レジスターファイルと呼び出し規約
RISC-V には ABI 名を持つ 32 個の整数レジスター(x0~x31)があります。
| レジスター | ABI 名 | 役割 | 保存する側 |
|---|---|---|---|
| x0 | zero | 常にゼロ | — |
| x1 | ra | 戻りアドレス | 呼び出し元 |
| x2 | sp | スタックポインター | 呼び出し先 |
| x3 | gp | グローバルポインター | — |
| x4 | tp | スレッドポインター | — |
| x5–x7 | t0–t2 | 一時レジスター | 呼び出し元 |
| x8 | s0/fp | フレームポインター | 呼び出し先 |
| x9 | s1 | 保存レジスター | 呼び出し先 |
| x10–x11 | a0–a1 | 引数/戻り値 | 呼び出し元 |
| x12–x17 | a2–a7 | 引数 | 呼び出し元 |
| x18–x27 | s2–s11 | 保存レジスター | 呼び出し先 |
| x28–x31 | t3–t6 | 一時レジスター | 呼び出し元 |
浮動小数点レジスター(F 拡張):f0~f31(引数用は fa0~fa7)。
2. 基本命令
# Arithmetic (R and I type)
add a0, a1, a2 # a0 = a1 + a2
sub a0, a1, a2 # a0 = a1 - a2
addi a0, a1, 42 # a0 = a1 + 42 (immediate)
mul a0, a1, a2 # a0 = a1 * a2 (M extension)
div a0, a1, a2 # signed divide (M extension)
rem a0, a1, a2 # remainder (M extension)
# Logical
and a0, a1, a2 # bitwise AND
or a0, a1, a2 # bitwise OR
xor a0, a1, a2 # bitwise XOR
sll a0, a1, a2 # shift left logical
srl a0, a1, a2 # shift right logical (unsigned)
sra a0, a1, a2 # shift right arithmetic (signed)
# Load / store
lw a0, 0(sp) # load word (32-bit)
ld a0, 0(sp) # load doubleword (64-bit, RV64)
lh a0, 4(sp) # load halfword (sign-extended)
lbu a0, 8(sp) # load byte (zero-extended)
sw a0, 0(sp) # store word
sd a0, 0(sp) # store doubleword (RV64)
# Branches (compare and branch)
beq a0, a1, label # branch if equal
bne a0, a1, label # branch if not equal
blt a0, a1, label # branch if less than (signed)
bltu a0, a1, label # branch if less than (unsigned)
bge a0, a1, label # branch if ≥ (signed)
# Jumps
j label # unconditional jump (pseudoinstruction: jal x0, label)
jal ra, func # jump and link (call)
jalr zero, ra, 0 # jump to ra (return: pseudoinstruction: ret)
3. 最小限の関数(psABI 呼び出し規約)
.section .text
.global add_numbers
# int add_numbers(int a, int b); — a in a0, b in a1, return in a0
add_numbers:
add a0, a0, a1 # result = a + b
ret # return (jalr zero, ra, 0)
.global factorial
# long factorial(int n); — n in a0
factorial:
addi sp, sp, -16 # allocate stack frame
sd ra, 8(sp) # save return address (RV64)
sd s0, 0(sp) # save s0 (callee-saved)
mv s0, a0 # s0 = n
li a0, 1 # default return 1
blez s0, .done # if n <= 0, return 1
addi a0, s0, -1 # a0 = n - 1
call factorial # recursive call: factorial(n-1)
mul a0, a0, s0 # a0 = result * n
.done:
ld ra, 8(sp) # restore ra
ld s0, 0(sp) # restore s0
addi sp, sp, 16 # deallocate
ret
4. ISA 拡張の命名規則
RISC-V 拡張は、ベース ISA の後ろに文字列として組み合わせます。
| 文字 | 拡張 | 説明 |
|---|---|---|
| I | Integer | ベースの 32/64 ビット整数(RV32I、RV64I) |
| M | Multiply | 整数の乗算と除算 |
| A | Atomic | アトミックなメモリー操作(lr/sc、AMO) |
| F | Float | 単精度浮動小数点 |
| D | Double | 倍精度浮動小数点 |
| C | Compressed | 16 ビット圧縮命令 |
| G | General | = IMAFD(省略表記) |
| V | Vector | ベクトル命令(SIMD) |
| Zicsr | CSR | 制御/ステータスレジスターへのアクセス |
| Zifencei | Fence.i | 命令フェンス |
| Zba/Zbb/Zbc/Zbs | Bit manipulation | ビット演算(B 拡張セット) |
| Ztso | TSO | Total Store Ordering メモリーモデル |
一般的なターゲット:
- 組み込み:
rv32imac— 浮動小数点なし、アトミック命令と圧縮命令あり - Linux アプリ:
rv64gc— 汎用拡張一式と圧縮命令 - 高性能:
rv64gcv— ベクトル拡張を追加
5. インラインアセンブリ(GCC/Clang)
// Read a CSR register (e.g., cycle counter)
static inline uint64_t read_cycle(void) {
uint64_t val;
asm volatile ("rdcycle %0" : "=r"(val));
return val;
}
// Atomic swap
static inline int atomic_swap(int *ptr, int new_val) {
int old;
asm volatile (
"amoswap.w.aqrl %0, %2, (%1)"
: "=r"(old)
: "r"(ptr), "r"(new_val)
: "memory"
);
return old;
}
// Memory fence
static inline void memory_fence(void) {
asm volatile ("fence rw, rw" ::: "memory");
}
// CSR read/write
#define csr_read(csr) ({ \
uint64_t _v; \
asm volatile ("csrr %0, " #csr : "=r"(_v)); \
_v; \
})
uint64_t mstatus = csr_read(mstatus);
6. 圧縮命令(RVC)
RVC は、次の条件を満たす場合、一般的な 32 ビット命令を 16 ビット版に置き換えます。
- レジスターが x8~x15 にある(
c.形式の場合) - 即値がより小さなフィールドに収まる
- 特定の命令パターンに一致する
# Enable C extension in GCC
riscv64-linux-gnu-gcc -march=rv64gc prog.c -o prog
# Check if compressed instructions were generated
riscv64-linux-gnu-objdump -d prog | grep "c\."
# c.addi, c.ld, c.sw, c.j, etc.
# Disable compressed (for debugging or targets without C)
riscv64-linux-gnu-gcc -march=rv64g prog.c -o prog
7. QEMU シミュレーションと GDB
# Install QEMU RISC-V
apt-get install qemu-user qemu-system-riscv64
# User-mode emulation (run RV64 binary on x86 host)
qemu-riscv64 ./prog
# System emulation (full bare-metal VM)
qemu-system-riscv64 \
-machine virt \
-nographic \
-kernel firmware.elf \
-gdb tcp::1234 \
-S # start paused
# GDB remote session
riscv64-linux-gnu-gdb prog
(gdb) target remote :1234
(gdb) load
(gdb) break main
(gdb) continue
RISC-V psABI 呼び出し規約の詳細については、references/riscv-abi.md を参照してください。
関連スキル
- AArch64 との比較には
skills/low-level-programming/assembly-armを使用します - x86-64 アセンブリには
skills/low-level-programming/assembly-x86を使用します - 実機の RISC-V デバッグには
skills/embedded/openocd-jtagを使用します - RISC-V のクロスコンパイル環境の設定には
skills/compilers/cross-gccを使用します
コメント
Markdown 対応