Skip to content

§ 4.3.1. Register<W>

Register<W> is the width-parametric, whole-word register used by the RV32I circuitry. It groups W FlipFlopCell storage bits under one write enable. RV32I uses it for the program counter, pipeline payloads, register-file words, request payloads, and compact control/status fields. It has no register addressing, byte mask, or memory transaction interface.

Supported widths are 1, 2, 3, 4, 5, 8, 16, and 32 bits. W is the number of bits in one stored value, not the number of registers; other widths are not registered. The embedded simulation uses Register<32> as a representative instance.

Component Description Structural Behavioral
Register<W> One resettable W-bit register with a whole-word write enable Yes Yes
ClockedStateBit One resettable bit that captures on every clock edge, for handshake state Yes Yes
FlipFlopCell Reused write-enabled, resettable storage bit Yes Yes

Storage and memory

The RV32I circuitry keeps state in two different kinds of component.

Storage (this section) Memory (chapter 3)
Access Every bit is used every cycle, in parallel One word at a time, chosen by an address
Width Any supported width, including 1 to 5 bits Whole bytes: 8, 16, or 32 bits
Write control One enable for the whole value An enable plus one mask bit per byte
Technology Flip-flops only Flip-flop, SRAM, DRAM, ROM, or NOR flash

Register<W> and ClockedStateBit are storage: the program counter, pipeline fields, flags, and handshake state have no address and are always flip-flops. Instruction and data memory are memory, and their technology can be chosen. The register file (4.3.2) sits between the two: its words are registers because they are only ever written whole, and the decoder and read selectors around them supply the addressing.

Storage components are catalogued as storage.register.W and storage.state-bit. FlipFlopCell is the flip-flop technology's memory cell (memory.ff.cell); registers are built from it, and the CPU also uses it directly for single flags.

4.3.1.1. Register<W>

IO pins

Pin Direction Width Meaning
D Input W Next value to capture
WE Input 1 Enable replacement of the entire word
CLK Input 1 Capture on a known low-to-high transition
RST Input 1 Active-high asynchronous reset to zero
Q Output W Currently stored value

There is no external address, byte mask, or request/response handshake. D is a proposed next value; changing it does not immediately change Q.

Behavior

Condition Effect
Power-up, before reset or capture Stored bits are unknown
RST = 1 Clear all W bits without waiting for a clock edge
RST = 0, rising CLK, WE = 1 Capture D in all W bits
RST = 0, rising CLK, WE = 0 Retain the previous value
No rising edge, with reset inactive Retain the previous value, including while CLK stays high

Reset has priority over writing. Before capture, consumers see the old Q; after capture and propagation, they see the new Q. A PC update does not need to read this new value to finish the instruction: Fetch supplies the current PC to the combinational datapath, and Next PC prepares the value for the later capture edge.

C++ example

This standalone reference covers every supported width, including initialization, edge detection, reset priority, write enable, and X/Z handling. Bits are least-significant first. Call evaluate after inputs change, preparing data and controls before a rising edge. It returns the settled result immediately; simulator scheduling, propagation delays, and history are omitted.

#include <array>
#include <cstddef>

namespace register_example {
enum class Bit { L, H, X, Z };
template<std::size_t W> using Word = std::array<Bit, W>;
Bit known(Bit b) { return b == Bit::L || b == Bit::H ? b : Bit::X; }

template<std::size_t W>
class Register {
    static_assert(W == 1 || W == 2 || W == 3 || W == 4 ||
                  W == 5 || W == 8 || W == 16 || W == 32);
    Word<W> stored;
    Bit previous_clock = Bit::X;
public:
    Register() { stored.fill(Bit::X); }
    Word<W> evaluate(const Word<W>& data, Bit write, Bit clock, Bit reset) {
        if (reset == Bit::H) stored.fill(Bit::L);
        else if (reset != Bit::L) {
            for (auto& bit : stored)
                if (bit != Bit::L) bit = Bit::X;
        } else if (previous_clock == Bit::L && clock == Bit::H) {
            for (std::size_t bit = 0; bit < stored.size(); ++bit) {
                if (write == Bit::H) stored[bit] = known(data[bit]);
                else if (write != Bit::L)
                    stored[bit] = stored[bit] == Bit::L && data[bit] == Bit::L
                        ? Bit::L : Bit::X;
            }
        }
        previous_clock = clock;
        return stored;
    }
};
} // namespace register_example

Unknown inputs and timing

Captured Z data becomes X. An uncertain reset (X or Z) preserves stored zeros and makes other bits unknown. With reset inactive, an uncertain write enable follows the FF cell's AND/OR feedback mux: old zero/new zero stays zero; every other pair becomes X, including old one/new one. This is intentionally not an optimistic merge of two candidate values.

The behavioral implementation recognizes only known CLK = 0 → 1 edges. Ambiguous clocks and simultaneous clock/data/control changes are outside the cross-fidelity timing contract; this is not an analog setup/hold or metastability model. Allow the structural data/enable paths to settle before capture. Structural and behavioral implementations are compared at settled checkpoints, not at each intermediate gate transition.

Implementation

Structural implementation

Register<W> is one whole-word FF register Register<W> directly contains W FlipFlopCell instances with one shared whole-word write enable. W=1 contains only BIT_0. Register<W> D · WE · CLK · RST → Q W × FlipFlopCell BIT_0 BIT_1 BIT_(W−1) … WE applies to all W bits · no byte mask
One word, W cells. The single write enable fans out to all cells; W=1 uses only BIT_0. No byte-mask gates or nested register are needed.
Part Connection / responsibility
INPUT_BITS Splits external D[W] into individual bits when W > 1
BIT_0–BIT_(W-1) W FlipFlopCell instances sharing WE, CLK, and RST
OUTPUT_BITS Joins cell outputs into Q[W] when W > 1

W=1 connects D and Q directly to BIT_0, without a splitter or joiner. There is no nested register wrapper: expanding any structural register directly reveals its W resettable FF cells. Selecting behavioral fidelity keeps one word-level state object.

Unlike FFWord<W>, this register has no byte mask or byte-enable gates. WE updates every bit together, including for sub-byte control fields. Both components reuse the same FF cell; they provide different write interfaces, not different storage technologies.

Behavioral implementation

RegisterBehavioral stores one W-bit logic vector and the previous clock value, applying the rules above. Catalog construction attaches gate-derived clock-to-output and setup timing; a supported delay twin can time the current state and inputs. The structural register can instead select behavioral FlipFlopCell children. Both choices retain the same external pins. Simulator output history supplies Q when scrubbing the timeline.

RV32I integration

Consumer Width / use
Single-cycle Fetch 32: PC_STATE, the current instruction address
Pipeline Fetch 32: PC_STATE, the next fetch address
IF/ID, ID/EX, EX/MEM, MEM/WB registers 32 for full-width fields; supported smaller widths for packed status, control, and register addresses
Execution status 4: TRAP_CAUSE_WORD
Memory requesters Retained request fields, sized to their actual payload widths
Register file 31 instances of Register<32>, one for each writable architectural register

Users select circuit::families::registerWord(W), registered as storage.register.W with W replaced by the supported width. The component is introduced here as an RV32I building block; its implementation is also reused by response buffers. Neither use changes its pins or adds a transaction protocol. The standalone structural source is Register.hpp and Register.cpp.

Examples

Every checkpoint of the 32-bit RegisterTest appears below, with checkpoint number and simulation time. It illustrates the same edge/reset/enable rules used at every supported width; RegisterWidthsTest checks the other widths. The rows are successive observations of one stateful sequence, not independent input combinations. Clock preparation occurs between checkpoints. Times belong to the embedded simulation's default profile, not a universal register latency.

All 56 recorded rows of RegisterTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (53c4401aa50a9337). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit DInput · 32 bit RSTInput · 1 bit WEInput · 1 bit QExpected output · 32 bit
#0reset9 0 0x00000000 1 0 0x00000000
#1hold-disabled21 1 0xFFFFFFFF 0 0 0x00000000
#2write-all-ones40 1 0xFFFFFFFF 0 1 0xFFFFFFFF
#3no-level-capture46 1 0x00000000 0 1 0xFFFFFFFF
#4write-zero60 1 0x00000000 0 1 0x00000000
#5write-aaaaaaaa80 1 0xAAAAAAAA 0 1 0xAAAAAAAA
#6write-55555555102 1 0x55555555 0 1 0x55555555
#7walking-bit-0124 1 0x00000001 0 1 0x00000001
#8walking-bit-1146 1 0x00000002 0 1 0x00000002
#9walking-bit-2168 1 0x00000004 0 1 0x00000004
#10walking-bit-3190 1 0x00000008 0 1 0x00000008
#11walking-bit-4212 1 0x00000010 0 1 0x00000010
#12walking-bit-5234 1 0x00000020 0 1 0x00000020
#13walking-bit-6256 1 0x00000040 0 1 0x00000040
#14walking-bit-7278 1 0x00000080 0 1 0x00000080
#15walking-bit-8300 1 0x00000100 0 1 0x00000100
#16walking-bit-9322 1 0x00000200 0 1 0x00000200
#17walking-bit-10344 1 0x00000400 0 1 0x00000400
#18walking-bit-11366 1 0x00000800 0 1 0x00000800
#19walking-bit-12388 1 0x00001000 0 1 0x00001000
#20walking-bit-13410 1 0x00002000 0 1 0x00002000
#21walking-bit-14432 1 0x00004000 0 1 0x00004000
#22walking-bit-15454 1 0x00008000 0 1 0x00008000
#23walking-bit-16476 1 0x00010000 0 1 0x00010000
#24walking-bit-17498 1 0x00020000 0 1 0x00020000
#25walking-bit-18520 1 0x00040000 0 1 0x00040000
#26walking-bit-19542 1 0x00080000 0 1 0x00080000
#27walking-bit-20564 1 0x00100000 0 1 0x00100000
#28walking-bit-21586 1 0x00200000 0 1 0x00200000
#29walking-bit-22608 1 0x00400000 0 1 0x00400000
#30walking-bit-23630 1 0x00800000 0 1 0x00800000
#31walking-bit-24652 1 0x01000000 0 1 0x01000000
#32walking-bit-25674 1 0x02000000 0 1 0x02000000
#33walking-bit-26696 1 0x04000000 0 1 0x04000000
#34walking-bit-27718 1 0x08000000 0 1 0x08000000
#35walking-bit-28740 1 0x10000000 0 1 0x10000000
#36walking-bit-29762 1 0x20000000 0 1 0x20000000
#37walking-bit-30784 1 0x40000000 0 1 0x40000000
#38walking-bit-31806 1 0x80000000 0 1 0x80000000
#39hold-final-walking-bit825 1 0xDEADBEEF 0 0 0x80000000
#40capture-unknown-bit847 1 0b000100100011010001X1011001111000 0 1 0b000100100011010001X1011001111000
#41reset-clear859 0 0b000100100011010001X1011001111000 1 1 0x00000000
#42unknown-reset-preserves-zero864 0 0b000100100011010001X1011001111000 X 1 0x00000000
#43unknown-write-enable-agrees885 1 0x00000000 0 X 0x00000000
#44reset-before-high-z894 0 0x00000000 1 X 0x00000000
#45capture-high-z-as-unknown909 1 0b00010010001101Z00101011001111000 0 1 0b00010010001101X00101011001111000
#46x-enable-agreed-ones950 1 0xFFFFFFFF 0 X 0bXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#47x-reset-priority-clock-high958 1 0xFFFFFFFF 1 X 0x00000000
#48x-enable-mixed-data979 1 0x00FF00FF 0 X 0b00000000XXXXXXXX00000000XXXXXXXX
#49x-reset-mixed-state1009 1 0xAAAAAAAA X 1 0bX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0
#50x-reset-recovery1017 1 0xAAAAAAAA 1 1 0x00000000
#51z-enable-agreed-ones1057 1 0xFFFFFFFF 0 Z 0bXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#52z-reset-priority-clock-high1065 1 0xFFFFFFFF 1 Z 0x00000000
#53z-enable-mixed-data1086 1 0x00FF00FF 0 Z 0b00000000XXXXXXXX00000000XXXXXXXX
#54z-reset-mixed-state1116 1 0xAAAAAAAA Z 1 0bX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0
#55z-reset-recovery1124 1 0xAAAAAAAA 1 1 0x00000000

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

Embedded simulation

The sequence checks reset, disabled writes, capture versus clock-high hold, alternating and walking-bit patterns, X/Z data, uncertain enables, and asynchronous reset priority/recovery. Expand the register to inspect its cells.

Open the full visualizer

Tests

Test Coverage
RegisterTest Registered structural/behavioral scenario; every bit, whole-word writes, hold, reset, selected X/Z cases
RegisterFFIntegrationTest Context-free 32-bit construction, structural cell hierarchy, fidelity choices, reverse history, and programs 03/05/07 in the single-cycle and advanced pipeline cores
RegisterDocumentationTest Compiles this exact width-parametric C++ example, checks every declared 32-bit scenario action, and replays those bit-lane checks at every supported width
RegisterWidthsTest Structural/behavioral equivalence at widths 1/2/3/4/5/8/16/32, including unknown values and reset/enable behavior
FlipFlopCellTest Reused lower-level storage-bit contract

4.3.1.2. ClockedStateBit

ClockedStateBit is one flip-flop with no write enable: it takes a new value on every rising clock edge. Handshake logic computes the next state on every cycle, so it needs no hold path. Use FlipFlopCell or Register<W> when a value must be kept across cycles.

IO pins

Pin Direction Width Meaning
D Input 1 Next state
CLK Input 1 Capture on a known low-to-high transition
RST Input 1 Active-high asynchronous reset to zero
Q Output 1 Current state

Behavior

Condition Effect
Power-up, before reset or capture The stored bit is unknown
RST = 1 Clear the bit without waiting for a clock edge
RST = 0, rising CLK Capture D; X or Z data is stored as unknown
No rising edge, with reset inactive Retain the previous value

Reset has priority over capture. An uncertain reset (X or Z) leaves a stored zero unchanged and makes a stored one unknown.

Implementation

Structural implementation

One DFlipFlop named STATE, with D, CLK, and RST wired straight in and Q straight out. Compared with FlipFlopCell, there is no write-enable multiplexer in front of the flip-flop.

Behavioral implementation

ClockedStateBitBehavioral stores one logic value and the previous clock level and applies the rules above. A catalog-selected instance uses clock-to-output and setup delays characterized from the structural state bit.

Where it is used

Consumer State bits
Memory requesters BUSY, SENT, ABANDONED
MemoryTransactionPort PENDING
MemoryResponseBuffer OCCUPIED

Users select circuit::families::ClockedStateBit, registered as storage.state-bit.

Examples

ClockedStateBit has no standalone simulation page. Its recorded behavior is visible inside the memory requester and transaction port simulations linked above: expand BUSY or PENDING to see the flip-flop.

Tests

Test Coverage
ClockedStateBitTest Reset, capture of one and zero, hold while the clock stays high or falls, and asynchronous reset, compared between the structural and behavioral implementations