Chapter 6 — Reps
Conditioning, not grading. This week’s reps are integration reflexes: reading a traffic table, running an arbiter and defending its victim, finding the interrupt-versus-polling crossover, proving that a radio is billed by the second, and budgeting an island that never sleeps.
Ground rules:
- Type every command yourself. No copy-paste from the chapter. Your fingers should learn
python3 code/qos_arbiter.py --total …andg++ -std=c++17 -O2cold, because in the project you will be running variations nobody wrote down for you. - Run everything. Every program in
code/is standard-library C++17 or Python 3 and runs on all three workbenches in Appendix A, including Workbench B with no install and no admin rights. Reading a table is not the same as watching a master starve. - Predict before you measure. Every rep that produces a number asks for your prediction first, in writing. The gap between your prediction and the output is the entire lesson; if you look at the output first, you have thrown the rep away.
- AI policy — explain, never source. You may ask a model to explain any mechanism in this chapter. You may not take a number, an interface name, or a capability claim from it. Anything you look up gets verified against a primary source — a vendor specification page, an architecture reference manual, or a standards document — and Rep 11 makes catching a fabricated figure the exercise itself. End each AI-touching rep with a one-line AI usage note in the format Appendix D defines.
- Log like it counts. Reps are not graded, but the ones that produce numbers are the rehearsal for
measurements.xlsx. Record what you ran, on what, how many times, and under what conditions, per Appendix C.
Everything below assumes you are in this chapter’s directory so that code/… paths resolve.
Reps 1–4: The Interconnect, and Who Waits
Rep 1 — Read the traffic table and predict the loser
Open code/bus-traffic.csv and read all seven rows. Note the first line: this is synthetic teaching data, and you must be able to say so out loud before you use it in any sentence.
cat code/bus-traffic.csv
python3 -c "print(sum([3600,2400,12000,9000,6000,600,3000]))" # sum of the peaks
Predict, in writing, before running anything else. With a total of 24,000 MB/s and strict priority by latency class: (a) which master is served first, (b) which master is the first to be denied, and (c) which master ends up with literally zero. Then write one sentence on why the deadline_ms column, not the peak_mbps column, decides the ordering.
Rep 2 — Fixed priority versus weighted fairness
Run both policies at the same supply:
python3 code/qos_arbiter.py --total 24000
Compare your Rep 1 prediction against the fixed-priority table. Then read the weighted-fair table and answer the question that matters: how many masters missed a deadline under a policy in which nobody was treated unfairly? Write down the display’s granted bandwidth, its burst time, and its deadline, and state in one sentence what a user would see. Finish with the sentence a design review would want from you: which policy would you ship, and who did you decide to sacrifice?
Rep 3 — Find the bandwidth cliff
The display is fine at 24,000 MB/s under fixed priority. Somewhere below that it is not.
for t in 24000 12000 6000 4000 3600 3000; do
echo "--- total $t ---"
python3 code/qos_arbiter.py --total $t --policy fixed | grep -E 'display|camera'
done
Predict first: at roughly what total does the display first miss its deadline under fixed priority? Now bisect to find it within 100 MB/s. Write down the cliff value, and explain why it is where it is — the answer is one number in the CSV and it should fall straight out of the arithmetic. Then repeat the bisection under --policy weighted and note how much higher the cliff sits. One sentence: what does the difference between the two cliffs cost the product?
Rep 4 — Buy the best-effort floor and find out who pays
Strict priority starved the CPU to zero, which is a frozen user interface. Reserve a floor and see where the bill lands:
python3 code/qos_arbiter.py --total 24000 --policy fixed --floor 0
python3 code/qos_arbiter.py --total 24000 --policy fixed --floor 10
python3 code/qos_arbiter.py --total 24000 --policy fixed --floor 20
python3 code/qos_arbiter.py --total 24000 --policy fixed --floor 35
Predict first: as the floor grows, which master’s verdict degrades first, and why that one? Then run it and record, for each floor value, the CPU’s grant and the NPU’s grant. Find the smallest floor at which the CPU clears its average of 2200 MB/s. Write two sentences: what that floor cost the NPU, and whether you would ship it. This is the project’s Hard-tier argument in miniature — the answer is a judgment, not a number.
Reps 5–7: Interrupts, Polling, and the Register That Vanishes
Rep 5 — Locate the interrupt versus polling crossover
Build and run the model:
g++ -std=c++17 -O2 -o irq_vs_poll code/irq_vs_poll.cpp
./irq_vs_poll
Predict first, without looking: at 10,000 events per second, roughly what fraction of a core does the interrupt path consume, and is it cheaper or more expensive than busy-polling? Now run it. Record the crossover rate the program reports and the interrupt path’s CPU busy fraction at that point. Then answer the question the table is really asking: the polled column has better latency at every single rate — so why is the mobile default still interrupts? One sentence, and it should contain the word sleep.
Rep 6 — Move the crossover with a fatter handler
The crossover is not a property of the universe. It is a property of your driver.
./irq_vs_poll 6.0 4.0 # double the interrupt overhead and the wake energy
./irq_vs_poll 1.5 1.0 # a lean handler on a cheap wake
./irq_vs_poll 12.0 8.0 # a genuinely sloppy one
Predict first: doubling the interrupt overhead — does the crossover roughly double, roughly halve, or stay put? Then run all three and tabulate the crossover rate for each. Write two sentences on the consequence for a platform team: what does a badly written driver do to the system’s power budget, not just to its own device? Note also what happens to the reported latency column, and why it barely moves.
Rep 7 — The register read the compiler is allowed to delete
No new file for this one — read and reason. Consider these two loops against a memory-mapped status register:
uint32_t* a = reinterpret_cast<uint32_t*>(0x1234'0000);
volatile uint32_t* b = reinterpret_cast<volatile uint32_t*>(0x1234'0000);
while ((*a & 1u) == 0) { } // (A)
while ((*b & 1u) == 0) { } // (B)
Write down, before you look anything up: which loop may the compiler legally turn into an infinite loop on a value it loaded once, and exactly what rule permits it? Then answer three follow-ups in one or two sentences each: (i) does adding volatile guarantee that a preceding store to a DMA descriptor has reached memory before the device is started, and if not, what does? (ii) why is volatile the wrong tool for a flag shared between two CPU threads, and what is the right one? (iii) name the three AArch64 barrier instructions from §6.3 and say which one you need when you have just changed system state that affects instruction fetch. AI usage: you may ask a model to explain the C++ rule; verify its answer against the standard or your compiler’s documentation, and say in your note which you checked.
Reps 8–10: Radios and the Always-On Island
Rep 8 — Batching versus compressing the same bytes
python3 code/radio_energy.py
Predict first, in writing: you are going to move 240 KiB either as sixty separate transfers or as one. Guess the ratio of unbatched to batched energy — order of magnitude is enough — and guess how much a 50% compression saves. Then run it.
Record all three joule figures and the two savings percentages. Then write the two sentences that carry the whole lesson: what the active s column is measuring, what the tail s column is measuring, and which of the two every byte-oriented optimization actually touches.
Rep 9 — The tail is the bill
Prove the claim by attacking it. Vary only the tail:
python3 code/radio_energy.py --tail-s 8
python3 code/radio_energy.py --tail-s 2
python3 code/radio_energy.py --tail-s 0.5
Then vary only the spacing, holding the tail fixed:
python3 code/radio_energy.py --tail-s 8 --interval-s 60
python3 code/radio_energy.py --tail-s 8 --interval-s 4
Predict first: what happens to the unbatched energy when the interval drops below the tail, and why? (Read the model’s overlap rule in code/radio_energy.py if you are unsure — but predict before you read.) Record the batched-versus-unbatched ratio for each run and write one sentence naming the condition under which batching stops helping. Finally: find, by bisection, the tail value at which compressing by 50% saves as much as batching does. Report it and say why that value is unrealistic for a real cellular link.
Rep 10 — Budget the always-on island
Paper and head. No new program.
A device must survive standby on a stated idle-power budget. You have five candidate always-on jobs: step counting from the accelerometer, wake-word detection, a raise-to-wake gesture, a barometer sample every second, and a background app that polls a server every five minutes.
Write down, before you reason further: which of the five can run entirely on the sensor hub, and which force the application processor awake? Then, for each of the five, state (a) what it wakes, (b) how often, and (c) one concrete change that reduces the wake count without removing the feature. Use §6.7’s FIFO-batching argument for at least two of them, and §6.9’s tail argument for the polling one.
Finish with the sentence the project’s Medium tier asks for: given a fixed idle-power target, which one job would you cut first, and what evidence would justify the cut? No numbers are required — this is a structural argument, and inventing a milliwatt figure you cannot cite would be exactly the failure this course grades hardest.
Rep 11: Integration Under Scrutiny
Rep 11 — Make an AI quote you an interface figure, then verify it
This rep is the week’s AI thread, and it is designed so that you catch something.
Ask a language model three questions of escalating specificity:
1. What is MIPI CSI-2 used for in a phone, and why does it have no back-pressure?
2. What are the five channels of an AXI4 interface?
3. What is the peak interconnect bandwidth of <a currently shipping SoC you name>?
Now go and check. Question 1 is a mechanism question and the model will probably do well. Question 2 is a named-fact question you can verify against the protocol specification. Question 3 is the trap: for most parts that figure is not published, and a model asked for it will very often produce one anyway.
Write down: the model’s answer to each, whether you could confirm it in a primary source, and the exact source you checked (name the document, not “the internet”). For any claim you could not confirm, quote the model’s sentence verbatim and mark it unverifiable. Then write the one sentence that will save you a rubric failure in the project: what is the difference between a figure a model explained and a figure a model sourced? AI usage: required — name the model and version, and state what you verified and what you rejected.
Done? One Last Thing.
This is the project in miniature — an integration map for one use case.
Take a single scenario: capturing 4K video while navigating on cellular, screen on. Do all four steps in one sitting:
- Enumerate the masters. List every bus master you believe is active. For each, name the interface it arrives on (from §6.6), whether it has a deadline, and what it shares. Aim for at least six.
- Run the contention. Use
code/qos_arbiter.pyat a total you choose, and justify the total in one sentence. Record which masters are in trouble. - Name the two most likely failures. Not “it might be slow.” Two specific integration failures, each with a mechanism — which master starves which, at which seam, producing which symptom the user actually perceives.
- Say what you would measure. For each of the two failures, one measurement that would confirm or refute it, and what result would refute it. A prediction you cannot refute is not an analysis.
Keep this write-up. It is the exact shape of the report.docx the project asks for, and you have already drafted its spine.
Up next: Project 6 — Project 6: The Integration Map.