Claude Skill

assembly-riscv

Use when reading or writing RV32/RV64 assembly, inline asm in C, the RISC-V psABI, IMAFD extension naming, compressed instructions, or QEMU RISC-V debugging.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download outlinedriven-outline-driven-development-.devin_skills_assembly-riscv-b0e8ce8.zip · 4 KB
Part of outlinedriven/outline-driven-development — 145 skills

Install

skills CLI npx skills add https://github.com/OutlineDriven/outline-driven-development/tree/main/.devin/skills/assembly-riscv
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install outlinedriven-outline-driven-development@llmmart
Git git clone https://github.com/OutlineDriven/outline-driven-development.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole outlinedriven/outline-driven-development collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

RISC-V assembly

RISC-V is a small load-store ISA grown through named extensions. This skill covers user-mode RV32 and RV64 assembly, the psABI register contract, and the QEMU loop.

Contract

Field Bound contract
Trigger The task writes inline asm or assembly for RV32 or RV64, decodes ABI register names, names an ISA extension string, enables compressed instructions, or debugs RISC-V under QEMU with GDB.
Authority Read-only. The skill explains and drafts; edits land through the normal coding path. No remote mutation.
Side effect None.
Done The drafted assembly assembles for the named base ISA and extensions, or the compiler output under discussion is explained register by register.

Inputs

  • The C or C++ source, assembly fragment, or compiler output: required.
  • The base ISA and extensions: required. The -march string such as rv64gc, written IMAFDC in ISA order.
  • The execution environment: required. QEMU virt for user or system work, hardware otherwise.

Procedure

  1. Map registers by role. The ABI names are what disassembly and assembly listings show. Done when: every register in the fragment is classified.
Register ABI name Role
x0 zero Hardwired zero, writes are discarded
x1 ra Return address
x2 sp Stack pointer
x3 gp Global pointer
x4 tp Thread pointer
x5 to x7, x28 to x31 t0 to t6 Temporaries, caller saved
x8 s0/fp Frame pointer or saved register, callee saved
x9, x18 to x27 s1, s2 to s11 Saved registers, callee saved
x10 to x17 a0 to a7 Arguments and return values, caller saved
f0 to f7, f28 to f31 ft0 to ft11 FP temporaries, caller saved
f8 to f9, f18 to f27 fs0 to fs11 FP saved, callee saved
f10 to f17 fa0 to fa7 FP arguments and returns, caller saved
  1. Write the calling convention into the code. Args go in a0 to a7, extra args on the stack, return value in a0. Save callee-saved registers before use and restore them before return. Done when: any s register used is saved and restored.

  2. Read the base instructions. Done when: each instruction in the fragment parses.

add  a0, a1, a2        # a0 = a1 + a2
sub  a0, a1, a2
mul  a0, a1, a2        # M extension
div  a0, a1, a2        # signed divide, remainder in rem
and  a0, a1, a2
or   a0, a1, a2
xor  a0, a1, a2
sll  a0, a1, a2        # shift left by register
slli a0, a1, 3         # shift by immediate
lw   a0, 0(a1)         # load word, RV32
ld   a0, 0(a1)         # load doubleword, RV64
lbu  a0, 0(a1)         # byte, zero-extended
sw   a0, 0(a1)
sd   a0, 0(a1)
beq  a0, a1, label     # branch if equal
blt  a0, a1, label     # signed less than
bge  a0, a1, label     # unsigned forms are bgeu/bltu with the u suffix
jal  ra, func          # call
jalr zero, ra, 0       # return, pseudo for ret
la   a0, symbol        # load address, pseudo
li   a0, 42            # load immediate, pseudo
  1. Write the minimal function shape. A leaf function that fits in registers needs no stack. Done when: every non-leaf function saves and restores what it uses.
.global factorial          # RV64 example
factorial:
    li   a1, 1             # result accumulator
1:  beqz a0, 2f
    mul  a1, a1, a0
    addi a0, a0, -1
    j    1b
2:  mv   a0, a1
    ret
  1. Name the ISA correctly. Extensions combine into one string in fixed order. rv64gc is the common application profile and expands to IMAFDC plus Zicsr and Zifencei. a brings atomics, m integer multiply and divide, f and d single and double float. Done when: the -march string matches the hardware or QEMU target.

  2. Write inline asm with the right constraints. RISC-V CSR access needs csrr or the csrrs family, and a memory clobber when the instruction has memory side effects. Done when: inputs, outputs, and clobbers are each listed.

static inline uint64_t rdcycle(void) {
    uint64_t val;
    __asm__ volatile("rdcycle %0" : "=r"(val));
    return val;
}
  1. Use compressed instructions where density matters. The C extension replaces common 32-bit encodings with 16-bit ones; enable it through -march=rv64gc or disable with -march=rv64ima. Verify with disassembly: compressed instructions print as c.addi, c.ld, and their c. family. Done when: the disassembly shows the intended encoding width.
riscv64-linux-gnu-gcc -march=rv64gc -O2 prog.c -o prog
riscv64-linux-gnu-objdump -d prog | grep -E '\sc\.'
  1. Debug under QEMU. Run QEMU with GDB waiting, then connect and break. Done when: breakpoints hit and registers read out.
qemu-riscv64 -g 1234 ./prog            # user mode
qemu-system-riscv64 -M virt -nographic -kernel fw_jump.elf -gdb tcp::1234 -S
riscv64-linux-gnu-gdb ./prog
(gdb) target remote :1234
(gdb) b main
(gdb) c

Failure and recovery

Failure class Behavior
f instructions fail to assemble The -march string lacks f or d. Extend it, for example rv64gc already carries both.
Atomics undefined The a extension is missing from -march, or the target truly lacks it. Add a or rewrite with a lock.
Corruption across a call A callee-saved s register was used without save and restore. Audit the prologue and epilogue.
QEMU hangs at boot The kernel or firmware image does not match the machine. Re-run with -nographic and read the early console output.
GDB cannot connect The port disagrees or QEMU lacks -g. Check the QEMU command line first, then the GDB target.

Output

Annotated assembly or inline asm with register roles, the exact -march string, and for QEMU work the exact launch and GDB commands. The full psABI table, including the floating-point calling variants, is in references/riscv-abi.md.

Files (outline-driven-development)
  • agents
    • openai.yaml 225 B
      interface:
        display_name: "Assembly Riscv"
        short_description: "Use when reading or writing RV32/RV64 assembly, inline asm in C, the RISC-V psABI, IMAFD extension naming, compressed instructions, or QEMU RISC-V debugging."
      
  • references
    • riscv-abi.md 3.1 KB
      # RISC-V psABI reference
      
      Source: the RISC-V ELF psABI specification maintained by riscv-non-isa.
      
      ## Integer calling convention
      
      | Register | ABI name | Role |
      |----------|----------|------|
      | `x0` | `zero` | Hardwired zero |
      | `x1` | `ra` | Return address, caller saved |
      | `x2` | `sp` | Stack pointer, callee owned |
      | `x3` | `gp` | Global pointer |
      | `x4` | `tp` | Thread pointer |
      | `x5` to `x7` | `t0` to `t2` | Temporaries, caller saved |
      | `x8` | `s0`/`fp` | Saved register or frame pointer, callee saved |
      | `x9` | `s1` | Saved register, callee saved |
      | `x10` to `x11` | `a0`, `a1` | Arguments and return values |
      | `x12` to `x17` | `a2` to `a7` | Arguments |
      | `x18` to `x27` | `s2` to `s11` | Saved registers, callee saved |
      | `x28` to `x31` | `t3` to `t6` | Temporaries, caller saved |
      
      Call sequence:
      
      1. Caller places up to eight integer args in `a0` to `a7`; further args go on the stack, each aligned to its size.
      2. Caller saves any `t` or `a` registers it needs across the call.
      3. Caller executes `jal ra, target`.
      4. Callee saves the `s` registers it will use and allocates its frame with a 16-byte-aligned `sp`.
      5. Callee returns the value in `a0` and restores `sp` and the saved registers.
      6. Callee executes `ret`, which is `jalr zero, ra, 0`.
      
      Stack frame layout at function entry: the incoming args above `sp` if any were passed on the stack, the return address at `sp - 8` once saved, and locals below the saved registers.
      
      ## Floating-point calling convention
      
      With `f` and `d` in the ISA, FP args go in `fa0` to `fa7`:
      
      | Register | ABI name | Role |
      |----------|----------|------|
      | `f0` to `f7` | `ft0` to `ft7` | Temporaries, caller saved |
      | `f8` to `f9` | `fs0`, `fs1` | Saved, callee saved |
      | `f10` to `f17` | `fa0` to `fa7` | Arguments and returns, caller saved |
      | `f18` to `f27` | `fs2` to `fs11` | Saved, callee saved |
      | `f28` to `f31` | `ft8` to `ft11` | Temporaries, caller saved |
      
      ABI variants selected by `-mabi`: `ilp32` and `lp64` pass floats in integer registers through software, `ilp32f` and `lp64f` use `f` registers for single precision, `ilp32d` and `lp64d` pass doubles in `f` registers. Hardware without FP uses the soft-float ABI.
      
      ## Atomics
      
      The `a` extension provides load-reserved and store-conditional loops for lock-free code:
      
      ```asm
      1:  lr.w t0, (a0)        # reserve the word
          addi t0, t0, 1
          sc.w t1, t0, (a0)    # t1 = 0 when the store landed
          bnez t1, 1b          # retry when the reservation broke
      ```
      
      The `zawrs` and later extensions change wait strategies but not this loop shape.
      
      ## Extension naming
      
      Extensions concatenate in the mandated order:
      
      | Letter | Extension |
      |--------|-----------|
      | `I` | Base integer ISA, RV32I or RV64I |
      | `M` | Integer multiply and divide |
      | `A` | Atomics |
      | `F` | Single-precision float |
      | `D` | Double-precision float |
      | `G` | Shorthand for `IMAFD` with Zicsr and Zifencei |
      | `C` | Compressed 16-bit instructions |
      
      Extra extensions, for example `B` for bit manipulation with its `Zba`/`Zbb`/`Zbs` sub-extensions (ratified 2021), append after the single-letter block. Write the full `-march` string and let the toolchain reject what the target lacks.
      
  • SKILL.md 6 KB
    ---
    name: assembly-riscv
    description: 'Use when reading or writing RV32/RV64 assembly, inline asm in C, the RISC-V psABI, IMAFD extension naming, compressed instructions, or QEMU RISC-V debugging.'
    ---
    
    # RISC-V assembly
    
    RISC-V is a small load-store ISA grown through named extensions. This skill covers user-mode RV32 and RV64 assembly, the psABI register contract, and the QEMU loop.
    
    ## Contract
    
    | Field | Bound contract |
    |---|---|
    | Trigger | The task writes inline asm or assembly for RV32 or RV64, decodes ABI register names, names an ISA extension string, enables compressed instructions, or debugs RISC-V under QEMU with GDB. |
    | Authority | Read-only. The skill explains and drafts; edits land through the normal coding path. No remote mutation. |
    | Side effect | None. |
    | Done | The drafted assembly assembles for the named base ISA and extensions, or the compiler output under discussion is explained register by register. |
    
    ## Inputs
    
    - The C or C++ source, assembly fragment, or compiler output: required.
    - The base ISA and extensions: required. The `-march` string such as `rv64gc`, written `IMAFDC` in ISA order.
    - The execution environment: required. QEMU `virt` for user or system work, hardware otherwise.
    
    ## Procedure
    
    1. Map registers by role. The ABI names are what disassembly and assembly listings show. Done when: every register in the fragment is classified.
    
    | Register | ABI name | Role |
    |----------|----------|------|
    | `x0` | `zero` | Hardwired zero, writes are discarded |
    | `x1` | `ra` | Return address |
    | `x2` | `sp` | Stack pointer |
    | `x3` | `gp` | Global pointer |
    | `x4` | `tp` | Thread pointer |
    | `x5` to `x7`, `x28` to `x31` | `t0` to `t6` | Temporaries, caller saved |
    | `x8` | `s0`/`fp` | Frame pointer or saved register, callee saved |
    | `x9`, `x18` to `x27` | `s1`, `s2` to `s11` | Saved registers, callee saved |
    | `x10` to `x17` | `a0` to `a7` | Arguments and return values, caller saved |
    | `f0` to `f7`, `f28` to `f31` | `ft0` to `ft11` | FP temporaries, caller saved |
    | `f8` to `f9`, `f18` to `f27` | `fs0` to `fs11` | FP saved, callee saved |
    | `f10` to `f17` | `fa0` to `fa7` | FP arguments and returns, caller saved |
    
    2. Write the calling convention into the code. Args go in `a0` to `a7`, extra args on the stack, return value in `a0`. Save callee-saved registers before use and restore them before return. Done when: any `s` register used is saved and restored.
    
    3. Read the base instructions. Done when: each instruction in the fragment parses.
    
    ```asm
    add  a0, a1, a2        # a0 = a1 + a2
    sub  a0, a1, a2
    mul  a0, a1, a2        # M extension
    div  a0, a1, a2        # signed divide, remainder in rem
    and  a0, a1, a2
    or   a0, a1, a2
    xor  a0, a1, a2
    sll  a0, a1, a2        # shift left by register
    slli a0, a1, 3         # shift by immediate
    lw   a0, 0(a1)         # load word, RV32
    ld   a0, 0(a1)         # load doubleword, RV64
    lbu  a0, 0(a1)         # byte, zero-extended
    sw   a0, 0(a1)
    sd   a0, 0(a1)
    beq  a0, a1, label     # branch if equal
    blt  a0, a1, label     # signed less than
    bge  a0, a1, label     # unsigned forms are bgeu/bltu with the u suffix
    jal  ra, func          # call
    jalr zero, ra, 0       # return, pseudo for ret
    la   a0, symbol        # load address, pseudo
    li   a0, 42            # load immediate, pseudo
    ```
    
    4. Write the minimal function shape. A leaf function that fits in registers needs no stack. Done when: every non-leaf function saves and restores what it uses.
    
    ```asm
    .global factorial          # RV64 example
    factorial:
        li   a1, 1             # result accumulator
    1:  beqz a0, 2f
        mul  a1, a1, a0
        addi a0, a0, -1
        j    1b
    2:  mv   a0, a1
        ret
    ```
    
    5. Name the ISA correctly. Extensions combine into one string in fixed order. `rv64gc` is the common application profile and expands to `IMAFDC` plus Zicsr and Zifencei. `a` brings atomics, `m` integer multiply and divide, `f` and `d` single and double float. Done when: the `-march` string matches the hardware or QEMU target.
    
    6. Write inline asm with the right constraints. RISC-V CSR access needs `csrr` or the `csrrs` family, and a `memory` clobber when the instruction has memory side effects. Done when: inputs, outputs, and clobbers are each listed.
    
    ```c
    static inline uint64_t rdcycle(void) {
        uint64_t val;
        __asm__ volatile("rdcycle %0" : "=r"(val));
        return val;
    }
    ```
    
    7. Use compressed instructions where density matters. The C extension replaces common 32-bit encodings with 16-bit ones; enable it through `-march=rv64gc` or disable with `-march=rv64ima`. Verify with disassembly: compressed instructions print as `c.addi`, `c.ld`, and their `c.` family. Done when: the disassembly shows the intended encoding width.
    
    ```bash
    riscv64-linux-gnu-gcc -march=rv64gc -O2 prog.c -o prog
    riscv64-linux-gnu-objdump -d prog | grep -E '\sc\.'
    ```
    
    8. Debug under QEMU. Run QEMU with GDB waiting, then connect and break. Done when: breakpoints hit and registers read out.
    
    ```bash
    qemu-riscv64 -g 1234 ./prog            # user mode
    qemu-system-riscv64 -M virt -nographic -kernel fw_jump.elf -gdb tcp::1234 -S
    riscv64-linux-gnu-gdb ./prog
    (gdb) target remote :1234
    (gdb) b main
    (gdb) c
    ```
    
    ## Failure and recovery
    
    | Failure class | Behavior |
    |---|---|
    | `f` instructions fail to assemble | The `-march` string lacks `f` or `d`. Extend it, for example `rv64gc` already carries both. |
    | Atomics undefined | The `a` extension is missing from `-march`, or the target truly lacks it. Add `a` or rewrite with a lock. |
    | Corruption across a call | A callee-saved `s` register was used without save and restore. Audit the prologue and epilogue. |
    | QEMU hangs at boot | The kernel or firmware image does not match the machine. Re-run with `-nographic` and read the early console output. |
    | GDB cannot connect | The port disagrees or QEMU lacks `-g`. Check the QEMU command line first, then the GDB target. |
    
    ## Output
    
    Annotated assembly or inline asm with register roles, the exact `-march` string, and for QEMU work the exact launch and GDB commands. The full psABI table, including the floating-point calling variants, is in `references/riscv-abi.md`.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related