The PPO dependency rules (18.1, rules 9–13) are only as precise as the definition of which registers each instruction reads, writes, and carries dependencies between. Tables 13–22 pin that down for every RV64GC instruction. Rather than re-print ~200 uniform rows, here is the default rule plus the complete catalog of exceptions — which is exactly the data structure your LSU’s dependency tracker needs.
The default rule
An instruction’s sources are its rs1/rs2/rs3 fields, its destination is rd, and it carries a dependency from every source to every destination. x0 never counts as a source or destination.
Every ordinary computational instruction — ADDI, ADD, SUB, shifts, logic, MUL/DIV family, W forms, FSGNJ, FMV, FCLASS — follows it exactly. Memory instructions additionally tag their sources: rs1 is an address source (A); a store’s rs2 is a data source (D).
The exceptions (the whole point of §18.3)
| Sources → Destinations | Dependency carry | |
|---|---|---|
| Loads (LB/LH/LW/LD/LBU/LHU/LWU, FLW/FLD) † | rs1ᴬ → rd | NONE |
| Stores (SB/SH/SW/SD, FSW/FSD) | rs1ᴬ, rs2ᴰ → (nothing) | No destination — nothing to carry to |
| JALR † | rs1 → rd | NONE |
| Branches | rs1, rs2 → (nothing) | They create CONTROL dependencies instead (PPO rule 11, stores only) |
| LR.W/D † | rs1ᴬ → rd | NONE (like a load) |
| SC.W/D † | rs1ᴬ, rs2ᴰ → rd (only if successful) | NONE; rd is a destination only on success |
| All AMOs (.W/.D) † | rs1ᴬ, rs2ᴰ → rd | NONE |
| FENCE / FENCE.I / ECALL / EBREAK | no sources, no destinations | — |
| CSRRW ‡ | rs1, csr* → rd, csr (*csr not a source if rd=x0) | rs1 → csr; csr → rd |
| CSRRS / CSRRC ‡ | rs1, csr → rd, csr* (*csr not a destination if rs1=x0) | csr and rs1 → csr; csr → rd |
| CSRRWI ‡ | csr* → rd, csr (*unless rd=x0) | csr → rd |
| CSRRSI / CSRRCI ‡ | csr → rd, csr* (*unless uimm=0) | csr → rd and csr |
| FP computational (FADD…FSQRT, FMA family) | rs1[, rs2[, rs3]], frm* → rd (*frm a source only when rm=111) | Default carry, plus flag accumulation |
| FP compares/min/max/converts | sources → rd, flags accumulate (NV, NX as applicable) | Default + accumulate |
Key: ᴬ address source · ᴰ data source · † carries no src→dst dependency · ‡ carries exactly what’s listed.
Hardware Designer Notes
For an out-of-order design, this section IS the rename-stage
specification of memory-model dependencies: the † rows tell you which
producer→consumer edges are ordering-relevant (none through loads/AMOs
themselves), and the A/D tags tell you which operand of a memory op each
incoming edge constrains. Practical test: the xor t1,a0,a0; add t2,t3,t1; lw …,(t2) fake-dependency idiom must block address issue — if your
zero-detection or move-elimination optimizes it away in the ordering
domain, you’ve broken PPO rule 9 while keeping dataflow correct. Keep
value optimization and ordering tracking separate.
Minimal Linux-boot hart MUST
- Track address/data source distinction in the LSU — PPO rules 9/10/13 key on it
- Treat consumer-of-load-rd as dependent on the load even though loads carry nothing themselves
- Model the CSR carry rules exactly (rs1→csr but not rs1→rd for CSRRW) if you reorder around CSR ops at all
- Honor frm as a source under rm=111 — a write to frm must order before dynamic-rounding FP ops
MAY simplify / trap-and-emulate
- Ignore all of this in an in-order core — program order subsumes every syntactic dependency
- Let FP flag updates accumulate out of order (they carry only flag→flag)
Check yourself — dependency listings
1.lw t0,(a0); sw t1,0(t0). Then: lw t0,(a0); add t2,t0,zero; lw t3,(t2). Which orderings hold?
2.Why is a successful SC's rd architecturally interesting for dependencies?
3.A sequence of FADD.S instructions all update the NX flag in fflags. Do they serialize on it?
4.When is frm a source register of an FP instruction?