Computer Architecture · Superscalar and Vector
Register File Ports and Operand Delivery
Why each extra read port on a register file costs area, latency, and energy, how banking and replication spread the cost, and what late operand collection buys a wide-issue machine.
A register file is a small memory with a wide, parallel access pattern, and its port count is the least visible and most expensive parameter in a superscalar design. The arithmetic is unforgiving. A machine that issues W operations per cycle, each with two source operands and one destination, needs 2W read ports and W write ports to sustain that rate without conflict. At W equal to four that is eight read ports and four write ports on a structure that must be accessed within one pipeline stage, and every port adds bitline, sense amplifier, and decoder hardware to every bit cell in the file.
The alternatives are to reduce the port count and accept conflicts, to split the file into banks or replicas, or to move the wide read later in the pipeline into a narrower structure. Each has a distinct cost, and most real designs combine all three.
What a port costs inside a bit cell
A static register file bit cell is a pair of cross-coupled inverters. Reading it requires an access transistor that connects the internal node to a bitline; writing requires transistors that pull the internal nodes in opposite directions. A single-ported cell uses one access path and must arbitrate reads against writes. A multi-ported cell replicates the access structure per port, so an eight-read-port cell is roughly eight times the area of a one-read-port cell plus the inverter pair, before accounting for the wires.
The wires dominate. Each read port needs its own pair of bitlines running the height of the array and its own sense amplifier at the bottom. Bitline capacitance per unit length is set by the process, and the delay to develop a readable differential on a long bitline is proportional to its capacitance and the current the cell can supply. Adding ports adds parallel bitlines, which increases total capacitance and, in a fixed-height array, reduces the width available per bitline, forcing taller arrays or narrower cells. The decoder that drives the wordlines also grows: each port has an independent wordline per row, so an eight-port file has eight wordlines per row, each needing a driver sized for the total load.
The energy cost follows the same arithmetic. A read on a bitline charges and discharges the bitline capacitance; a write drives a full swing on the write bitlines. Dynamic energy per access scales with the number of bitlines that switch, so a wider machine’s register file consumes more energy per cycle even when the extra ports are idle, because precharge and wordline activation still happen. This is a large part of why wide cores are less energy-efficient per instruction than narrow ones.
Because the read delay sits inside a pipeline stage, the port count also affects frequency. A stage containing register read, bypass selection, ALU, and write-back has to fit the register read into a fraction of the cycle. Eight ports on a large file can push that fraction past what the cycle allows, and the designer’s options are to lengthen the stage, to split the read into two stages, or to reduce ports and accept conflicts. Issue Width and the Structural Ceiling on Instructions per Cycle treats this as one of the width limits; here it is the mechanism.
Banking: splitting by register number
Banking divides the register file into several independent arrays and uses low-order bits of the register number to choose among them. Two registers whose numbers differ in the bank bits live in different banks and can be read simultaneously even though each bank has only one read port. A file of 64 registers split into four banks of 16 gives each bank one read port and the file as a whole four reads per cycle, at the area of four small single-ported arrays rather than one four-ported array.
The failure mode is a bank conflict: two operands in the same bank. With two source operands per instruction and B banks, the probability that a random pair collides is 1/B. For B equal to four, one in four instruction pairs conflicts and must be serialised over two cycles or dispatched a cycle later. For a wide machine dispatching four operations per cycle, the expected number of conflicts per cycle is higher, and the scheduler must either check bank assignment at dispatch, which costs logic and delay, or accept occasional replays.
Two refinements reduce the conflict rate. The register allocator can be made bank-aware, assigning registers so that values frequently used together land in different banks; this requires the compiler to know the banking function, which makes banking part of the ISA contract or requires a hint field. Alternatively, the replication of one bank’s contents in another, or a small crossbar between banks, resolves a fraction of conflicts at the cost of area and delay. The crossbar’s cost grows with the square of the bank count, so it is used sparingly.
Replication: copies near the consumers
Replication takes the opposite approach. Instead of one file with many ports, the design instantiates several copies of the file, each with a small number of ports, and wires each copy to a subset of the functional units. Writes are broadcast to every copy, or written to one and copied on demand.
Replication makes reads cheap and writes expensive, and its area is the sum of the copies. Two copies of a 32-entry, two-read-port file cost roughly twice a single such file, but each copy’s read path is short because it sits beside its units, which helps the critical path. The write broadcast must reach every copy in the same cycle, so the write path becomes the long wire, and its delay grows with the distance between copies. This is why replicated register files are common in designs where the units are clustered and reads dominate, and why the cluster’s write path is the parameter that limits cluster count.
Clustering also changes the scheduler’s job. An operation that needs an operand produced in another cluster must either wait for the value to be copied or read it across the cluster boundary through a slower path. Compilers and dynamic schedulers both need a policy for this, and the policy’s quality shows up as a few percent of issue slots lost rather than a clean stall.
Late collection and the collector buffer
The third option moves the wide read out of the dispatch path. Instead of reading source operands at dispatch, the machine records which physical registers each waiting instruction needs, and when a result is written back it is broadcast to the waiting instructions, which capture it into a small per-instruction buffer near the functional unit. When all operands are present, the instruction issues and reads its operands from that buffer rather than from the register file.
The buffer is small, one or two entries per source operand per instruction in flight, and it is written by the result bus rather than read by a wide decoder, so its port structure is different: many narrow write ports from the functional units, few read ports to the ALU inputs. The register file then needs only the ports required for instructions that read a value not produced recently, which is a minority of operands in a steady-state loop.
The price is a wakeup mechanism. Every result must be compared against the tags of every waiting instruction, which is a set of comparators proportional to the number of entries times the number of result buses. That comparison is the classic wakeup-select loop, and it grows with the square of the issue width if every entry can be woken by every bus. Implementations limit it by segmenting the scheduler into smaller queues, each watching a subset of the result buses, at the cost of delaying an instruction that must move between segments.
The collector also changes the bypass network. If operands are captured into a buffer, the value must be routed from the producing unit to the consumer’s buffer, and the number of such routes is units times consumers, a quadratic again. The bypass network is frequently the largest single contributor to the core’s wire area, and it is a large part of why issue width changed little between the mid-2000s and the following decade.
Choosing a structure
The design space reduces to a set of questions with concrete answers.
If the workload has few independent operations per cycle, ports beyond four or six buy nothing, and a banked file with a conflict rate of a few percent is cheaper than a fully ported one. If the workload is numeric and wide, the compiler can schedule around bank conflicts, so banking with a known function works well. If the machine is clustered, replication with a carefully designed write path is the natural choice, and the cluster count is set by the write delay.
The energy argument favours late collection: reading a value once from a narrow buffer is cheaper than reading it from a wide, highly ported array on every access. The area argument favours banking. The frequency argument favours replication with short local wires. Real cores mix all three, and the mixture is one of the least portable parts of a microarchitecture, since it is tuned to a specific process and a specific clock target rather than to an instruction set.
For the compiler, the visible consequences are the number of registers it may keep live and the cost of spills. A spill to a stack slot costs a store and a load, each of which occupies a port and a load/store queue entry; on a machine with tight port budget, spilling hurts more than the instruction count suggests. Software Pipelining and Loop-Carried Dependences discusses how the schedule interacts with register pressure, and Simultaneous Multithreading and the Slots It Fills covers what happens when several threads compete for the same ports.
References
- Register File Design Considerations in Dynamically Scheduled Processors — Keith I. Farkas, Norman P. Jouppi, and Paul Chow, HPCA 1996
- The Microarchitecture of Superscalar Processors — Michael D. Smith and Gurindar S. Sohi, Proceedings of the IEEE, 1995
- Energy Dissipation in General Purpose Microprocessors — Ricardo Gonzalez, Benjamin M. Gordon, and Mark A. Horowitz, IEEE Journal of Solid-State Circuits, 1996