A microarchitecture spec is the document that describes how a processor will be built: its pipeline, its structures, its interfaces and its behavior in every corner. It sits between the architecture, which says what the processor must do, and the RTL, which says exactly how. Writing it forces decisions before code is written, and it becomes the shared reference for designers, verification engineers and software teams. This article explains what goes in a spec, how to write each part so that it can be checked, and how specs go wrong.
13 min read | Intermediate
What You'll Learn
- Explain what a microarchitecture spec is for and who reads it
- List the main sections and what each must state
- Write precise behavior, including corner cases
- Keep a spec, its RTL and its tests in step
Why Write One
A processor is built by many people over years, and each block depends on the exact behavior of its neighbors. If the fetch unit's author and the decoder's author remember a conversation differently, the result is a bug. A written spec is a way of making the decision once, in one place, and of letting everyone check it.
Writing it has another purpose. Many design problems only show up when you try to describe the design exactly. What happens if a store is flushed while waiting for the bus? If you can't write the answer, you have not designed it. Finding this in a document costs an hour. Finding it in silicon can cost millions.
Three levels of description should not be confused:
- The architecture (the ISA) is the contract with software. For a RISC-V core, it's the published specifications, plus a list of which options are implemented.
- The microarchitecture spec says how this design meets it: pipeline stages, queues, timing, and internal interfaces.
- The RTL is the implementation.
ISA, Microarchitecture and Implementation explains these layers. For a whole chip, the equivalent document is the one in The System Architecture Spec.
Who Reads It
A spec has several audiences, each with its own needs. RTL designers build from it. Verification engineers derive their test plans and checkers from it, so each statement must be testable. Performance architects compare it with their model. Physical designers read the block sizes and the critical timing paths. Software and compiler teams want instruction latencies and pipeline restrictions. A good spec is organized so that each can find their part.
What It Contains
| Section | What it states |
|---|---|
| Overview and goals | target PPA, target workloads, what is out of scope |
| ISA support | base and extensions, optional CSRs, unsupported cases and how they trap |
| Pipeline | the stages, what happens in each, the bypass network, stall and flush rules |
| Front end | fetch width, predictor type and sizes, BTB, return stack, recovery |
| Execution | the units, the latency of each operation, issue rules |
| Memory system | cache sizes, ways, line size, policies, store buffer, ordering, TLBs |
| Traps | priorities, entry and exit timing, interrupt latency |
| Interfaces | bus protocols, signals, timing, reset behavior |
| Debug and performance counters | what is observable, trigger support, event list |
| Power | clock gating, power states, what must be saved |
Writing Precisely
The most important skill is writing statements that can only be read one way. Compare:
vague: The cache handles misses quickly.
better: A load that hits the L1 data cache returns data to dependent
instructions after 2 cycles. On a miss, the cache sends one
read request for the line to the L2 interface. Up to 2 misses
may be outstanding; a third miss stalls the load pipe.
The second version states numbers, conditions and what happens at the limit. A verification engineer can write a test for each part. A good test of any sentence in a spec is: how would I check this?
A few habits help:
- State the limits. Every queue has a size, and every spec should say what happens when it is full.
- Use "shall" for requirements, and keep them apart from background explanation.
- Use tables and state diagrams for anything with several cases. A table shows missing cases, while prose hides them.
- Number the requirements, so that tests can refer to them.
- Say why. A short note on the reason for a decision saves someone from undoing it later.
Corner Cases
A spec earns its value in the corners, which is where bugs live. For each behavior, ask what happens if two things happen together. A list for a simple pipeline might include:
- A taken branch and a load-use stall in the same cycle: which has priority? Load-Use Stalls and Control Hazards gives the answer.
- An interrupt arrives during a multi-cycle divide: is it finished, or restarted?
- A misaligned load crosses a page boundary, and the second page faults: what does
mtvalreport? - A
wfiwith an interrupt already pending. - A CSR write that turns on interrupts, followed by an instruction that traps.
- A store in the queue when the core is reset.
- Memory returns an error response to an instruction fetch on a wrong path.
The last is a good example. An error on a wrong-path fetch must not trap, because that instruction was never really executed. The spec must say so, and a test must try it. The pipeline must carry the error along with the instruction, as Traps and Precise Exceptions explains.
Quick Check
Q: Improve this spec line: "The store buffer drains when possible."
Show Answer
For example: "The store buffer holds up to 4 committed stores. The oldest entry is written to the data cache in the first cycle in which the cache is not serving a load. When the buffer is full, a store at commit stalls the pipeline." It now gives a size, a rule, a priority and the full case.
An Example: The Load Pipeline
Here is a short sample section for a simple core, to show the level of detail. It covers the case a reader would otherwise have to guess:
3.4 Load execution
R3.4.1 A load computes its address in EX.
R3.4.2 The D-cache tag and data arrays are read in MEM.
R3.4.3 On a hit, data is forwarded from the end of MEM.
An instruction that uses the data in the next cycle stalls 1 cycle.
R3.4.4 Sub-word loads are aligned and extended in WB.
R3.4.5 On a miss, the pipeline freezes until the line's critical word arrives.
R3.4.6 A misaligned load takes a load address misaligned exception
(cause 4), and mtval holds the faulting address.
R3.4.7 A load to a device region is not speculated: it waits until all
earlier instructions have committed.
Each line can be turned into a test. The last rule says something that isn't obvious: reading a device register can have a side effect, so a load to it cannot run on a wrong path. Memory-Mapped I/O explains why.
Performance Targets
A spec should also state its performance intent: the load-to-use latency, the misprediction penalty, the sustained bandwidth to L2, the expected IPC on named workloads. Those are things that can be lost by accident. A small RTL change may add a cycle to a critical path, or let a queue fill up. If the target is written down, and a test measures it, the regression is seen at once. Performance Modeling describes how this is checked.
Where Does This Matter?
On a large project, the spec is a living set of documents, with an owner for each section and formal reviews. Designers present their block's spec to the architecture and verification leads before any RTL is written. In the open-source RISC-V world, core projects publish a user manual of just this kind, with pipeline diagrams, CSR tables and corner-case behavior, because outside users need it to integrate the core. Safety-critical products go further: every requirement must trace to a test, and the trace is audited.
Reviews
A spec review is a cheap way to find bugs. Good reviewers bring a particular point of view. A verification engineer asks how each statement could be checked. A physical designer asks whether it fits in a cycle. A software engineer asks whether it can be used. It helps to have a checklist of common omissions: reset values, full conditions, error paths and power states. The Microarchitecture Review Checklist offers one.
Keeping It Alive
The danger is drift. The RTL changes during the project, the spec doesn't, and soon nobody trusts it. Several things help:
- Change control. A change to behavior must update the spec, as a part of the same review.
- Generation. CSR maps, register fields and parameter tables can be generated from one source, which then produces the RTL, headers and documents, so that they cannot disagree.
- Assertions. Rules written as assertions in the RTL check the spec on every simulation. A rule such as "no more than 2 outstanding misses" becomes a line of code that fails if it is broken.
- Traceability. Each numbered requirement links to the tests that cover it.
A spec doesn't need to be long. A small core's spec might fill twenty pages. It must be complete and true: a short accurate document is far better than a long stale one.
Quick Check
Q: A designer finds that the RTL handles a corner case differently from the spec, but all the tests pass. What should happen?
Show Answer
Decide which is right, then fix that one, the RTL or the spec. Add a test for the case, since no test covered it. Leaving them different means that the next person will rely on the wrong one.
Starting From a Blank Page
When a new project starts, the spec is usually written in passes. The first pass is a page or two: the goals, the target workloads, the rough pipeline, and the main risks. It is cheap to change, and it gets the team to agree on direction. The second pass adds the structures, with sizes taken from the performance model, and the block diagram. The third adds the interfaces and detailed behavior, by which point different people own different sections, and reviews become more formal. RTL usually starts during the third pass, so the two develop together, and writing the RTL always finds gaps in the spec.
It helps to record decisions that were considered and rejected, with the reason. A year later, someone will suggest the same idea again, and a short note saves the discussion. A decision log can be as simple as a list at the end of the document: date, decision, alternatives, reason. It is often the most useful part of a spec for anyone joining the project late.
Finally, write the assumptions down, not only the conclusions. A cache size chosen because "the main workload fits in 32 KB" is safe only as long as that stays true. If the workload changes, a stated assumption tells the team what to check again.
A last check is to hand the document to someone new, and ask them what they would build. Wherever they have to guess, the spec has a gap.
Some teams keep the spec as plain text in the same repository as the RTL, so that a change to behavior and the matching change to the document go through one review.
Common Mistakes
Mistake #1: Describing only the normal case
Most of a spec's value lies in full queues, errors, resets and simultaneous events. A spec with only the normal flow leaves the hard decisions to whoever writes the RTL.
Mistake #2: Copying the RTL into words
A spec that repeats signal-level detail goes out of date quickly and hides the intent. Describe behavior and interfaces, and let the RTL hold the implementation.
Mistake #3: Forgetting the software view
Instruction latencies, pipeline restrictions and errata matter to compilers and drivers. Include what they need, or they will find out by measuring.
Practice Exercise
Challenge: Write a Spec Section
Write a short spec section for a return address stack in a simple core. It has 4 entries and is used in the fetch stage. Cover: (a) when it pushes and pops; (b) what happens when it is full; (c) what happens when it is empty; (d) what happens on a misprediction; (e) what happens at reset. Then name two tests that your statements imply.
Hint
Look at Static Prediction, BTBs and Return Address Stacks for the behavior. Make each statement testable.
Solution
A good answer might read: "R1: A fetched instruction predicted as a call (jal or jalr with rd = ra) pushes PC + 4. R2: A fetched instruction predicted as a return (jalr x0, 0(ra)) pops the top entry and uses it as the predicted target. R3: A push to a full stack overwrites the oldest entry. R4: A pop from an empty stack gives no prediction, and fetch continues at PC + 4. R5: On a flush, the stack pointer is restored to the value saved with the mispredicted branch; entry contents are not restored. R6: After reset the stack is empty." Tests: a call chain six levels deep, checking that the last four returns are predicted and the first two mispredict once each; and a wrong-path call followed by a flush, checking that later returns stay correct. Note that a return prediction is only a guess, so an error here costs cycles and cannot produce wrong results.
Summary
- A microarchitecture spec records how the design meets the ISA, before the RTL is written.
- It covers goals, pipeline, front end, execution, memory, traps, interfaces, debug and power.
- Write statements that can be tested, and state every limit and full condition.
- Corner cases, where two events happen together, are where a spec earns its value.
- Keep it true with change control, generated tables, assertions and traceability.