6.5.11 · D4Advanced & Emerging Architectures

Exercises — RISC-V custom extensions for accelerators

2,670 words12 min readBack to topic

This page assumes the parent note RISC-V custom extensions and its prerequisites — if a symbol here feels unearned, it was built there.

Some encoding problems use this picture of a 32-bit instruction word. Keep it open:

Figure — RISC-V custom extensions for accelerators

How to read the figure. A RISC-V instruction is exactly 32 bits, numbered 31 (leftmost, most significant) down to 0 (rightmost, least significant). Hardware finds the opcode first, so read right-to-left: the opcode is the rightmost 7 bits inst[6:0]. The remaining fields occupy fixed bit ranges — memorise these exact positions, because every hand-encoding problem below depends on them:

Field Bit range Width Selects
funct7 inst[31:25] 7 operation
rs2 inst[24:20] 5 source register 2 (data)
rs1 inst[19:15] 5 source register 1 (data)
funct3 inst[14:12] 3 operation
rd inst[11:7] 5 destination register (data)
opcode inst[6:0] 7 which instruction family

The two chalk-blue fields (funct3, funct7) pick which operation runs; the three chalk-pink fields (rs2, rs1, rd) pick which data it works on.


Level 1 — Recognition

Recall Solution — L1.1

The four reserved custom opcodes are custom-0 = 0001011, custom-1 = 0101011, custom-2 = 1011011, custom-3 = 1111011. Answer: (b) 0001011 = custom-0. The others are standard: 0110011 is OP (register ALU / R-type), 0000011 is LOAD, 1100011 is BRANCH. You never touch those — the whole point is that custom code lives where standard software never emits.

Recall Solution — L1.2

The rd field (destination register), 5 bits wide, occupying inst[11:7] (see the table above). It chooses data destination, not operation. The operation-selecting fields are funct3 (inst[14:12]) and funct7 (inst[31:25]).


Level 2 — Application

Recall Solution — L2.1

The operation is chosen by funct3 (3 bits) and funct7 (7 bits). Register fields pick data, not operation, so they don't count. Answer: 1024 distinct ops per slot.

Recall Solution — L2.2

Answer: 4096.

Recall Solution — L2.3

MMIO: cycles. Custom instruction: cycles. Ratio . Answer: MMIO spends cycles, custom spends ; the custom instruction is cheaper in overhead. This is exactly the "coupling tightness is the whole point" claim from the parent note, made numeric.


Level 3 — Analysis

Recall Solution — L3.1

Amdahl's Law: if fraction of runtime is accelerated by factor , total speedup is Ideal (): Realistic (): Answer: at most ; realistically . The lesson: even infinite acceleration of a 40% kernel can't beat . This is why you profile first — accelerating a small-fraction kernel is silicon wasted.

Recall Solution — L3.2

(A) → tightly-coupled. Operands fit in registers, latency is tiny, and it's invoked constantly, so per-invocation dispatch overhead must be near-zero. Route rs1,rs2 to a new functional unit, write rd back like any ALU op. (B) → loosely-coupled (RoCC). 4 MB does not fit in registers; the unit needs its own memory/DMA port and runs thousands of cycles. Dispatch a command, let the CPU continue, collect the result later. The data volume vs latency tradeoff decides it: small+short → tight, large+streaming → loose.


Level 4 — Synthesis

Recall Solution — L4.1

Fill each field (using the bit ranges from the figure table):

  • funct7 = 0000000inst[31:25]
  • rs2 = x7 = 00111inst[24:20]
  • rs1 = x6 = 00110inst[19:15]
  • funct3 = 000inst[14:12]
  • rd = x5 = 00101inst[11:7]
  • opcode = custom-0 = 0001011inst[6:0]

Concatenate MSB→LSB (spaces just separate fields, they are not part of the word): 0000000 00111 00110 000 00101 0001011 As one 32-bit word: 00000000011100110000010100001011. Now regroup those 32 bits into nibbles of 4 (from the left): 0000 0000 0111 0011 0000 0101 0000 1011 = 0 0 7 3 0 5 0 B = 0x0073050B. Answer: 0x0073050B.

Sanity check by place value. Each field contributes (its value) shifted left by its starting bit: at bit 0, at bit 7 gives , , at bit 15 gives , at bit 20 gives , . Sum , and . ✓

On the three-source subtlety. Standard R-type semantics read rs1, rs2 and only write rd — the encoding gives you exactly two source register addresses. But mac needs a third source: the old value of rd. Nothing in the R-type encoding names a third source. So the extension resolves it by a microarchitectural convention: your custom decode logic treats the rd field as both a read address and a write address (a read-modify-write of the same register), adding an extra register-file read of rd. You are not inventing a new bit-field — you are giving the existing rd field a second meaning inside your unit only. (Alternatives real designs use: dedicate a fixed accumulator register, or add an accumulator as internal state in a RoCC unit so no third register-file port is needed.)

Recall Solution — L4.2

Baseline: instructions, valid for any . Accelerated, a multiple of 4: there are full groups, each costing loads mac issues: General (the edge case). Write with quotient and remainder . The elements run in full mac groups. The leftover elements cannot fill a 4-wide mac, so you finish them with a scalar tail loop at the baseline cost of instructions each:

  • Check : issues (matches ).
  • Check : issues; speedup lower than the clean , because the tail is unaccelerated. As the tail's fixed cost is negligible and the speedup approaches . Answer: clean case ; with a remainder the tail loop drags the ratio down, exactly as shown for . The gain comes from hiding 4 μops behind one issue slot (SIMD width), not from the opcode itself. Compare with Vector / SIMD extensions (RVV), whose runtime-set vector length removes this hand-written tail-loop headache.

Level 5 — Mastery

Recall Solution — L5.1

Amdahl: . Candidate 1 (, ): Candidate 2 (, ): Whole-program: Candidate 1 gives ; Candidate 2 gives only , despite its flashier kernel figure. Per engineer-month benefit: C1 buys speedup / 6 months per month; C2 buys / 1 month per month. C1 still wins on both raw benefit and cost efficiency. Answer: build Candidate 1 first. The deciding factor is fraction of runtime how much you cut it, capped by the un-accelerated remainder — not the kernel-only multiplier. This is the parent note's "benefit = frequency × cycles saved − cost, Amdahl-limited" made concrete. See Hardware accelerators vs general-purpose CPUs and Domain-specific architectures for the same reasoning at chip scale.

Recall Solution — L5.2

Their core decodes custom-0, finds no functional unit for it, and raises an illegal-instruction trap. The program stops (or the OS handles the trap) — it does not silently execute a different operation. That is the safety guarantee: an unimplemented custom op traps, it never returns a wrong result. Portability of standard binaries is untouched, because standard software never emits custom-0..3. Fragmentation here is purely a toolchain matter (each core needs its own intrinsics/assembler support), not a correctness break — exactly the steel-man in the parent note.


Connections