Back to components

Sequential Components

Clock-driven components with synchronous reset and enable signals. State that persists across clock cycles.

Clock Reset Enable Flip-Flop Register

Clock-Driven Design

Sequential logic is what gives digital circuits memory. While combinational logic computes outputs purely from current inputs, sequential components capture and hold values at specific moments in time, determined by a clock signal. On each rising edge of the clock, the circuit samples its inputs and updates its internal state. Between clock edges, outputs remain stable regardless of input changes.

This distinction is fundamental: combinational logic answers "what should the output be given these inputs right now?" while sequential logic answers "what should the output be given these inputs and everything that happened before?" Counters, shift registers, state machines, and pipeline registers all rely on sequential behavior.

PropertyCombinationalSequential
ClockNot usedRequired (rising edge triggered)
StateNoneStored in flip-flops/registers
Output depends onCurrent inputs onlyCurrent inputs + stored state
RHDL keywordbehaviorsequential
ResetNot applicableSynchronous reset to known state

The Sequential Block

In RHDL, the sequential keyword declares a clock-driven block. You specify the clock signal, an optional reset signal, and reset values for all stateful outputs. On each rising clock edge, all assignments within the block take effect simultaneously. When reset is asserted, outputs snap to their declared reset values.

d_flip_flop.rb
class DFlipFlop < RHDL::Sim::Component
  input  :clk, :rst
  input  :d,   width: 8
  output :q,   width: 8

  # Sequential block: updates on rising edge of clk
  # Synchronous reset sets q to 0
  sequential clock: :clk,
             reset: :rst,
             reset_values: { q: 0 } do
    q <= d
  end
end

An enabled register only updates its value when the enable signal is high. This is essential for building pipeline registers that can be stalled, or data registers that should hold their value until explicitly written.

enabled_register.rb
class EnabledRegister < RHDL::Sim::Component
  input  :clk, :rst, :en
  input  :d,   width: 16
  output :q,   width: 16

  sequential clock: :clk,
             reset: :rst,
             reset_values: { q: 0 } do
    # Only update when enable is asserted
    q <= cond(en, d, q)
  end
end

Registers and Counters

Registers and counters are the most common sequential building blocks. A counter increments (or decrements) on each clock cycle, optionally wrapping around or stopping at a limit. These are used everywhere: address generators, timers, clock dividers, and control sequencers.

A parameterized up-counter with configurable width and maximum value. When the counter reaches its limit, it wraps back to zero.

counter.rb
class Counter < RHDL::Sim::Component
  parameter :WIDTH,  default: 8
  parameter :MAX,    default: 255

  input  :clk, :rst, :en
  output :count,  width: WIDTH
  output :wrap

  sequential clock: :clk,
             reset: :rst,
             reset_values: { count: 0 } do
    wrap <= count.eq?(MAX) & en

    count <= cond(en,
      cond(count.eq?(MAX), 0, count + 1),
      count)
  end
end

A shift register that shifts data in serially and provides parallel output. Useful for serial-to-parallel conversion, SPI interfaces, and delay lines.

shift_register.rb
class ShiftRegister < RHDL::Sim::Component
  parameter :WIDTH, default: 8

  input  :clk, :rst
  input  :serial_in
  input  :shift_en
  input  :load_en
  input  :parallel_in, width: WIDTH
  output :parallel_out, width: WIDTH
  output :serial_out

  sequential clock: :clk,
             reset: :rst,
             reset_values: {
               parallel_out: 0
             } do
    serial_out <= parallel_out[WIDTH - 1]

    parallel_out <= cond(load_en,
      parallel_in,
      cond(shift_en,
        { parallel_out[WIDTH - 2..0], serial_in },
        parallel_out))
  end
end

Timing Considerations

Understanding timing is critical for reliable sequential designs. Every flip-flop has setup and hold time requirements that constrain how fast the clock can run. RHDL's sequential model abstracts away the analog details but the designer must still reason about timing at the architecture level.

ConceptDescription
Setup timeData must be stable before the clock edge. Limits maximum clock frequency based on the longest combinational path between registers.
Hold timeData must remain stable after the clock edge. Ensured by minimum propagation delay through combinational logic.
Clock-to-Q delayTime from clock edge until the register output is valid. Adds to the critical path of downstream logic.
Synchronous resetReset is sampled on the clock edge, like any other input. RHDL uses synchronous reset by default for cleaner synthesis.
Asynchronous resetReset takes effect immediately, regardless of the clock. Used when the circuit must reach a known state without waiting for a clock edge (e.g., power-on).
MetastabilityWhen signals cross clock domains, they must be synchronized using multi-stage flip-flop chains to avoid metastable states.

A two-stage synchronizer for safely crossing clock domains. The input signal is sampled twice through flip-flops in the destination clock domain, dramatically reducing the probability of metastable values propagating into the design.

synchronizer.rb
class Synchronizer < RHDL::Sim::Component
  input  :clk, :rst
  input  :async_in
  output :sync_out

  # Internal pipeline stage
  signal :meta

  sequential clock: :clk,
             reset: :rst,
             reset_values: {
               meta: 0,
               sync_out: 0
             } do
    # Two flip-flop synchronizer chain
    meta     <= async_in   # first stage (may go metastable)
    sync_out <= meta       # second stage (resolved)
  end
end