5.1.8 · D3Instruction Set Architecture (ISA)

Worked examples — RISC-V extensions (M, A, F, D, V, C)

4,074 words19 min readBack to topic

This deep dive lives under the parent topic on RISC-V extensions. The parent told you what the M, A, F and D extensions do. Here we prove you can survive every case those instructions can throw at you — every sign, every zero, every overflow, every rounding trap.

Before we start, one rule for reading this page: whenever a symbol shows up, we say what it means in plain words first.


The scenario matrix

Every trap in these four extensions falls into one of these cells. Our examples below are labelled with the cell they cover, so you can check nothing is missing.

# Cell (case class) Extension Why it can bite you
C1 Positive × positive, need both halves M 32×32 gives 64 bits; forgetting MULH loses the top
C2 Signed × signed, one negative M sign bits propagate into the upper half
C3 Both negative (product positive) M naive "two negatives" reasoning at the bit level
C4 Mixed signed × unsigned M MULHSU — the exam favourite
C5 Divide-by-zero / signed overflow M RISC-V does not trap; it returns defined values
C6 Atomic increment, contended A LR/SC retry loop
C7 Atomic compare-and-swap A building CAS from LR/SC
C8 Exact float add (power-of-two operands) F no rounding — the "clean" case
C9 Inexact float add (0.1 + 0.2) F rounding error, the classic bug
C10 Single vs double precision drift F/D accumulation over many adds
C11 Degenerate float: zero, and F two zeros, one value

Prerequisites you may want open: 5.1.07-RISC-V-base-ISA, 5.2.03-IEEE-754-floating-point, 3.3.06-Instruction-encoding, 6.4.02-Cache-coherence-protocols.

Figure — RISC-V extensions (M, A, F, D, V, C)

Read the map (figure s01). Along the top row are the three families of scenarios: the lavender M box splits a 64-bit product into two halves, the mint A box shows the reservation dance, the butter F/D box shows the align-add-round pipeline. Each top box has an arrow dropping to a coral box of the exact case numbers (C1–C5, C6–C7, C8–C11) we will walk. Use this as your checklist — every coral cell gets its own worked example below.


M Extension worked examples

Why does a multiply need two instructions?

Figure — RISC-V extensions (M, A, F, D, V, C)

Read the multiplier picture (figure s02). Two lavender 32-bit inputs rs1 and rs2 feed the mint "multiplier array"; its arrow points down to a butter bar labelled "full 64-bit product". From that bar, two arrows fan out: the left one to the coral MULH box (bits [63:32]) and the right one to the lavender MUL box (bits [31:0]). The line at the top, product = 2^32 * MULH + MUL, is the only equation you need for every M example — the picture just shows where each half physically comes from.







A Extension worked examples

Two acronyms drive this whole section, so let's pin them down before any code:

We will also meet two RISC-V pseudo-instructions in the code:

Figure — RISC-V extensions (M, A, F, D, V, C)

Read the reservation timeline (figure s03). The top coral row is core A's timeline, the bottom lavender row is core B's, running left to right in time. Both start with lr.w -> 5 (each reads the counter value 5 and reserves the address). Core A's sc.w 6 OK fires first; the vertical "invalidate" arrow dropping onto core B's row is the key moment — that store kills core B's reservation. So core B's sc.w FAIL box does nothing, and the mint boxes show core B looping back: lr.w -> 6 (now the fresh value) then sc.w 7 OK. The butter bar at the bottom, counter: 5 -> 6 -> 7, is the moral: no update was lost.



F / D Extension worked examples

Recall the single-precision layout from the parent and 5.2.03-IEEE-754-floating-point: where is the sign bit, the 23-bit mantissa, the 8-bit biased exponent.

Figure — RISC-V extensions (M, A, F, D, V, C)

Read the float pipeline (figure s04). Four boxes flow left to right, each an arrow to the next: lavender unpack (split each number into ), mint align (shift the smaller number's mantissa so both share one exponent), butter add mantissas, coral normalize + pack (put the sum back into form and write the bits 0x3FE00000). The caption underneath, "no bits fall off the shift → no rounding → exact", is why this particular example (C8) is clean: the align step in the mint box loses nothing.





Recall Self-test

M — why do we need MULH at all? ::: A 32×32 product is up to 64 bits; MUL gives the low 32, MULH the high 32. M — what does DIVU x, 0 return on RISC-V? ::: 0xFFFFFFFF (all ones); no trap. A — what does LR/SC stand for? ::: Load-Reserved / Store-Conditional. A — why does core B's sc.w fail after core A stores? ::: Core A's write invalidated core B's reservation on that line. A — what does CAS mean? ::: Compare-And-Swap: overwrite only if memory still holds the expected value. F — is 1.5 + 0.25 exact? ::: Yes, both and their sum are finite sums of powers of two. F — is 0.1 + 0.2 == 0.3? ::: No; 0.1 and 0.2 aren't exact in binary, so rounding differs. F — what is a ULP? ::: The gap between one representable float and the next; the smallest change from flipping the lowest mantissa bit. F — do +0 and -0 compare equal? ::: Yes, numerically equal; bits differ.