Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
    • Controller and Datapath
      • Split Design: Intent First
      • Signal Contract Between FSM and Datapath
      • Two-Process FSM Style
      • Coding Rules That Prevent Latent Bugs
      • Worked Example: GCD
      • FSM Bug Stories
    • Moore vs Mealy
    • State Encoding
    • Multi-Cycle Sequencers
    • UART Transmitter
    • Memory Controller
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Controller and Datapath
  1. Topics
  2. RTL Design
  3. FSM & Control
  4. Controller and Datapath
  5. Signal Contract Between FSM and Datapath

Part 4 · FSM & Control · Intermediate

Signal Contract Between FSM and Datapath

Control signals and status flags at the controller-datapath boundary.

On this page

    Why this matters

    Two blocks that write the same register show up as X in simulation or as a multiple-driver error in synthesis. A short signal table written first catches most integration bugs before any RTL exists.

    After this lesson you can: write the signal table between a controller and a datapath, and state the contract rules for control, status and acceptance.

    The signal table

    The two blocks share only a short, explicit signal list. Writing it first catches most integration bugs before any RTL exists.

    Signal

    Direction

    Meaning

    When valid

    load

    controller → datapath

    Load a_in and b_in into the registers

    IDLE && start at an acceptance edge; reset inactive

    sub_a

    controller → datapath

    a <= a - b at the next edge

    Only when a > b

    sub_b

    controller → datapath

    b <= b - a at the next edge

    Only when a < b

    a_gt_b

    datapath → controller

    Status: a is greater than b

    Continuously driven; meaningful for the current operation in RUN after load

    a_eq_b

    datapath → controller

    Status: a equals b

    Same RUN-phase validity; operands are unreset and may be X before load

    start, done

    outside ↔ controller

    Command offer and completion

    Accept start only in IDLE; done high for the DONE cycle

    result

    datapath → outside

    Completed GCD

    Valid with done and held until the next accepted load, unless reset cancels validity

    Contract rules

    With reset inactive, acceptance is state == IDLE && start at a rising edge; operands must be positive and meet setup/hold there. Offers in RUN or DONE are ignored, with no queue. A pulse confined to DONE is lost; a held start is accepted at a later IDLE edge and can launch another transaction. Reset cancels outstanding work and invalidates the result; this datapath deliberately does not reset its operand registers.

    • Controls are decoded from state (and status), flags come from registers. Neither side reads the other's internal registers.

    • One cycle, one meaning. Control signals are valid for exactly the cycle in which they are asserted; the datapath acts at the next edge.

    • Mutually exclusive controls (sub_a and sub_b) are never asserted together; the datapath gives them a defined priority anyway.

    • Status flags are combinational from registers, so they describe the current values. The controller reacts in the same cycle.

    • No hidden timing: if the datapath needs two cycles for something, add a status signal (ready) instead of assuming.

    A run in time

    Sequence diagram of the controller loading the datapath, then issuing subtract commands until the flags show equality.
    One run of the GCD machine as a sequence of control and status exchanges.

    The status flags can feed the controller combinationally, which makes the controller's outputs depend on datapath values within the same cycle. That is fine for a single block, and it is the reason the datapath comparators sit on the controller's critical path.

    Common mistakes

    Warning

    Mixing up who owns a register is the classic integration bug. If both the controller and the datapath write a, you have two drivers in simulation (X) or a multiple-driver error in synthesis. Exactly one block drives each signal.

    Check yourself Why must sub_a and sub_b never be asserted together?

    Both would try to change the operands at the same edge, and in the datapath one has priority over the other, so the result would silently ignore a command. Define mutually exclusive controls and assert only one.

    Remember

    • Exactly one block drives each register. Neither side reads the other's internal registers.

    • Controls are valid for one cycle and the datapath acts at the next edge.

    • If the datapath needs two cycles, add a ready status signal instead of assuming timing.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Two-process FSM style

    Previous

    90 / 350

    Next: Two-Process FSM Style

    Up next

    Up nextTwo-Process FSM Style

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Coding Rules That Prevent Latent Bugs
    • Worked Example: GCD
    • FSM Bug Stories