Introduction
The register file is an often-overlooked component of computer architectures. In simple terms, the register file is where the CPU holds the values it's operating on.
In a modern RISC architecture, the instructions typically take the form:
add r0, r1, r2 // r0 = r1+r2
where r0, r1 and r2 are numerical identifiers of registers in the register file. The addressable registers in the instruction set are typically defined as an array of registers some neat power of two in size: SPARC and MIPS have 32 registers, each 32-bit. DEC Alpha has 32 registers of 64 bits each.
In a RISC processor, generally any register can be used for any purpose, so it can be thought of as a regular array:
________________________________
r0: |________________________________|
r1: |________________________________|
...: ~________________________________~
rn-1:|________________________________|
rn: |________________________________|
Generally in processors, the register file physically takes the form of a regular two-dimensional array of memory elements, much as you'd probably expect.
This is of course a fairly simplified view of it: Often there are more registers in the register file than immediately visible: for example, duplicates for interrupt handling and function calls; many architectures have register addresses which don't actually require registers on the register file, for example SPARC, MIPS and Alpha all have a virtual 'zero' register which always reads as the value zero, and thus doesn't have any register state associated with it.
The logical structure of a register file is fairly straightforward: each read or write port on the register file has a decoder to convert a binary register number into an array of enable signals (one per register) and a bus which each register can read (for a write port) or drive (for a read port).
A register in a typical 32-bit RISC such as an early MIPS core would look a lot like the following:
________
RdAEn ---->| |
RdBEn ---->| |=32=> RdDataA
WrEn ---->| |=32=> RdDataB
WrData =32=>|________|
The register file as a whole includes decoders for each of the three enable signals, and multiple instances of the register, likely using a single common data bus for each read and write port.
Design considerations
Like all things in computer architecture, designing a register file is a case of carefully balancing the parameters of the design to best meet whatever design goals are set out.
Factors which affect the design of a register file for a processor implementation, and instruction set:
Size
- Program register use
-
Algorithmically, it's desirable for a processor to have as many registers as possible. A large register file reduces the need for accesses outside the processor core (eg. to memory or cache), and hence the number of operations needed to shuffle values around. Also, having a large register set available allows more parallelism in the algorithm to be exploited (eg, statically by software pipelining or dynamically by register renaming in architectures where a smaller number of registers are defined architecturally.)
- Die area
Naturally a larger register file will take up more space on the chip, making it larger and more power-hungry, and also reducing wafer yield.
- Access speed
The time required to access a register in the register file is effected by the number of registers in it: to decode the register address, and drive the signals to or from the correct register is approximately log2(n). All but the largest register files, however, can usually happily be accessed within a single cycle.
- Instruction Encoding
Part of the reason that register files tend to be, architecturally, a nice round power of two in size is that it makes the best possible use of the bits necessary in the instruction required to encode the destination register. To address 32 registers (SPARC, MIPS), 5 bits are required in the instruction encoding (since 2^5 = 32) for each register the instruction uses. For a typical 3-register operation that's 15 bits out of a 32 bit instruction encoding.
Read/Write requirements
Another important design factor for a register file is the number of read and write ports on the register file.
A simple three-operand addition instruction implies a register file operation for each operand. To re-examine the previous example,
add r0, r1, r2 // r0 = r1+r2
the three register operations required to execute this instruction are:
If the register file were capable of supporting only a single operation per cycle, merely executing this simple instruction would take at least three cycles.
Thankfully, multiple operations can be performed on the register file at the same time (there's nothing in the fundamental nature of a register to prevent this), however each read or write port requires it's own set of decoding and value distribution logic connected to the register file. Multiple ports on a register file add significantly to both the die area and the access time, but have the advantage of allowing higher throughput.
Sources: too many random papers and discussions to point to explicitly.