VHDL · Chapter 7.4 · Sequential Logic Design
Registers and Register Banks
A flip-flop stores one bit, while a register stores a word. It is simply N D flip-flops sharing the same clock, capturing a whole standard logic vector on each edge. Stack many registers into an array and you have a register bank, also called a register file, which is addressable storage written by selecting one register and read by multiplexing one out. This is the heart of every CPU datapath and many controllers. This lesson shows how to model both, how a register file becomes an array of flip-flops with write-address decoding and read multiplexing, and the patterns that keep them clean and synthesizable, built directly on the D flip-flop.
Foundation14 min readVHDLRegisterRegister FileFlip-FlopStorageRTL
1. Engineering intuition — a word-wide flip-flop, then many
If a D flip-flop is one bit of memory, a register is a row of them clocked together — present a word on the input, and at the edge the whole word is captured and held. Nothing new happens per bit; the bits just share a clock. Go one step further and arrange many registers in an array, and you get a register bank: storage you select by address. That is how a CPU keeps its working registers, how a controller holds a set of configuration values, and how small fast memories are built — all from the same flip-flop repeated.
2. Formal explanation — register and register bank
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- A REGISTER: N D flip-flops sharing a clock (a vector assigned on the edge).
reg : process (clk)
begin
if rising_edge(clk) then
if rst = '1' then q <= (others => '0');
else q <= d; -- capture the whole word
end if;
end if;
end process;
-- A REGISTER BANK / FILE: an array of registers; write by address, read combinationally.
type regfile_t is array (0 to 15) of std_logic_vector(31 downto 0);
signal rf : regfile_t;
wr : process (clk)
begin
if rising_edge(clk) then
if we = '1' then rf(to_integer(unsigned(waddr))) <= wdata; end if; -- write one register
end if;
end process;
rdata <= rf(to_integer(unsigned(raddr))); -- combinational read (a mux over the array)A register is a vector signal assigned under rising_edge(clk) — N flip-flops with a shared clock.
A register bank is an array of such vectors (lesson 2.10): a clocked write selects one
element by address (we enables it), and a read indexes the array combinationally — effectively a
mux selecting one register's contents.
3. Production RTL — a two-read, one-write register file
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- Classic CPU-style register file: two combinational read ports, one clocked write port.
type rf_t is array (0 to 31) of std_logic_vector(31 downto 0);
signal rf : rf_t := (others => (others => '0'));
write_port : process (clk)
begin
if rising_edge(clk) then
if we = '1' then rf(to_integer(unsigned(waddr))) <= wdata; end if;
end if;
end process;
rdata_a <= rf(to_integer(unsigned(raddr_a))); -- read port A (mux)
rdata_b <= rf(to_integer(unsigned(raddr_b))); -- read port B (mux)What hardware does this become? An array of 32 thirty-two-bit registers (flip-flops), a write-address decoder enabling exactly one register on a write, and two read multiplexers selecting any two registers combinationally. This is the canonical RISC register file. (Large memories are usually inferred as block RAM instead — a later FPGA topic — but the modeling pattern is the same array-of-registers idea.)
4. Hardware interpretation — N flip-flops, addressed
5. Simulation interpretation — write on edge, read continuously
Register bank: a clocked write lands on the edge; reads are combinational
8 cycles6. Debugging example — the write/read race and the missing enable
Expected: a value written this cycle is reliably stored and readable. Observed: an unexpected
old/new value on a same-cycle read, or every register changes on a write. Root cause: for the
read/write timing, a combinational read of the just-written address returns the old value (the write
lands on the edge, the read is combinational) — a read-during-write hazard to define deliberately; for
"every register changed," the write lacked an address-selected enable, so the assignment hit the
whole array instead of one element. Fix: decide and document read-during-write behaviour (often
forward the write data if needed), and gate the write with we plus an address so exactly one register
updates. Engineering takeaway: a register file writes one addressed register on the edge and reads
combinationally — guard the write with an enable and address, and define read-during-write explicitly.
-- Write exactly ONE register, only when enabled.
if rising_edge(clk) then
if we = '1' then rf(to_integer(unsigned(waddr))) <= wdata; end if; -- one element, enabled
end if;7. Common mistakes & what to watch for
- Writing the whole array instead of one element. Index by the write address and gate with
we. - Ignoring read-during-write. A combinational read of the address being written returns the old value on that edge; define the intended behaviour.
- Index out of range.
to_integer(unsigned(addr))must stay within the array bounds; size the address to the array. - Resetting a large register file every cycle. Often unnecessary and costly; reset only what needs a defined initial value (or rely on write-before-read).
- Expecting a huge array of registers to be cheap. Large storage should usually be inferred block RAM (later); flip-flop arrays suit small, multi-port register files.
8. Engineering insight & continuity
Registers and register files are the D flip-flop scaled up: a register is flip-flops sharing a clock, a register file is an addressable array of them with write decoding and read multiplexing. Keep the clocked write narrow (one enabled, addressed element) and the read combinational, and the structure synthesises to exactly the storage you intend. Most registers, though, should update only sometimes — on a condition, not every clock — which is the subject of the next lesson, Clock Enables: how to make a register hold or update under control, the gateway to counters, accumulators, and pipelines with stalls.