6.5.11 · D5Advanced & Emerging Architectures

Question bank — RISC-V custom extensions for accelerators

1,367 words6 min readBack to topic

True or false — justify

Adding a custom instruction changes the RISC-V ISA and therefore breaks portability of standard binaries.
False — custom ops live in reserved opcode space that the standard toolchain never emits, so an ordinary standard binary is untouched and still runs correctly.
A core that does not implement your custom instruction will silently compute a wrong result if it encounters it.
False — an unimplemented opcode raises an illegal-instruction trap; you get a clean fault, never a silent wrong answer, so correctness is preserved.
The four custom opcodes are guaranteed to never collide with a future ratified standard extension.
True — the spec permanently reserves custom-0..custom-3 for non-standard use, so standard extensions are forbidden from claiming them.
Custom instructions must be built as R-type.
False — R-type is the natural template because the register file already has two read ports and one write port, but you can shape a custom opcode into other formats; R-type just minimises datapath changes.
Using more custom instructions always increases performance.
False — net benefit = frequency × cycles-saved − (area + verification + Amdahl-limited ceiling); hardening a rarely-used op wastes silicon and buys almost nothing.
A tightly-coupled unit and a memory-mapped peripheral offer the same latency once the accelerator itself is fast.
False — MMIO adds hundreds of cycles of bus round-trip per invocation regardless of the unit's speed, while a tightly-coupled unit gets operands straight from registers in a few cycles.
The funct3 and funct7 fields select which data the instruction operates on.
False — funct3/funct7 select which operation runs; the register fields rs1, rs2, rd select the data.
RoCC (Rocket Custom Coprocessor) instructions can access memory independently of the CPU's normal load/store path.
True — a loosely-coupled coprocessor has its own memory/DMA port, so it can stream a whole buffer while the CPU continues other work.
The whole point of a custom instruction over a separate chip is coupling tightness.
True — operands arrive from registers and results go straight back inside the pipeline, eliminating the address-setup / bus / polling latency an off-CPU device would impose.

Spot the error

"I'll accelerate a function that runs 0.1% of the time so my chip is faster overall."
The error is ignoring Amdahl's Law — speeding up a tiny fraction of runtime yields near-zero total gain; profile first and only harden the hot 20% (Amdahl's Law).
"My MAC needs to accumulate, so I'll just make R-type read rd — that's the standard R-type behaviour."
The error is calling it standard: R-type normally reads only rs1/rs2. Making rd also a source (read-modify-write) is a microarchitectural design choice you encode yourself, not default semantics.
"There are 4 custom opcodes and 10 free funct bits, so I have custom instructions."
The error is adding instead of multiplying — each slot independently holds ops, giving , not .
"I'll place my dot-product accelerator on the memory bus so any device can reach it — best of both worlds."
The error is choosing MMIO for a fine-grained op; the per-call bus overhead dwarfs the compute, killing the speedup. MMIO fits only coarse, infrequent, large-data offload.
"Since I added instructions, I must be careful they don't overwrite existing standard opcodes."
The error is misplaced worry — reserved custom space cannot overlap standard opcodes by definition; the real concern is toolchain support, not opcode collision.
"The speedup comes from the opcode being custom."
The error is crediting the opcode itself; real gains come from hiding many μops (e.g. a SIMD-wide computation) behind one issue slot — the encoding is just the trigger.

Why questions

Why does RISC-V hand out empty opcode space instead of forcing you to fork the ISA?
So you extend the hardware/software contract without breaking it — existing software still runs, and you add your ops legally without a vendor's permission.
Why is R-type preferred when the register file already limits you to two source reads?
Because most useful accelerator ops take two operands and produce one result, which maps exactly onto the two read ports and one write port — so hazard/forwarding/decode logic barely changes.
Why does talking to an off-CPU accelerator cost hundreds of cycles?
Because each call needs address setup, a bus transaction, and polling or interrupt handling — all of which a register-to-register custom instruction skips entirely.
Why choose a loosely-coupled coprocessor over a tightly-coupled unit for a matrix multiply?
The operands (a whole matrix) don't fit in a couple of registers and the op runs for many cycles, so you want an independent memory port and overlap with the CPU — the data-volume-over-latency tradeoff favours RoCC.
Why must you provide compiler intrinsics or assembler .insn directives for a custom instruction?
Because standard compilers never emit reserved opcodes on their own; the intrinsic is how high-level code like c = mac(a,b,c) gets mapped to your single instruction.
Why is fragmentation a toolchain concern rather than a correctness concern?
Because each custom core needs its own intrinsics/support to use the ops, but any core lacking them either never emits them (standard binary) or traps cleanly — the program never silently misbehaves.
Why do the register fields not count toward the "encoding budget" of operations?
Because rs1/rs2/rd name which data, not which operation; only funct3/funct7 distinguish operations, so only their bits size the op count.

Edge cases

What happens when a custom instruction is executed on a core that lacks its accelerator?
It raises an illegal-instruction trap; the OS/handler can then emulate it in software or terminate cleanly — never a corrupt result.
If two teams both pick custom-0 with funct3=000, funct7=0 for different ops, is that a spec violation?
No — it's legal in the spec (the space is theirs to define) but their binaries are mutually incompatible; the collision is a toolchain/agreement problem, not an ISA rule break.
Can a single custom instruction legitimately behave like a whole loop internally?
Yes — that's exactly the win: one issue slot can drive a SIMD-wide or multi-cycle computation, hiding many μops, as long as the microarchitecture implements that behaviour.
Is the speedup ever below 1 (a slowdown) after adding a custom instruction?
Yes — if the op is rare its area/verification cost buys nothing and can hurt clock frequency or complicate the pipeline, so overall throughput or design efficiency can drop.
What if your accelerator needs three source operands but R-type gives only two read ports?
You must either add a read port (extra hardware), make one operand implicit (like rd in a MAC accumulate), or dispatch via a coprocessor interface — plain R-type alone cannot supply three sources.
When is memory-mapped I/O actually the right choice over a custom instruction?
For coarse, infrequent, large-data offload where the per-call bus overhead is negligible compared to the work done — there the independent device model wins (Memory-mapped I/O).

Connections