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

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
    • A Protocol Is a Contract
    • Roles and Agents
    • Signals, Meaning and Timing
    • Reading Timing Diagrams
    • Protocols as State Machines
    • Topologies and Duplex
    • Layering and the End-to-End Argument
    • Memory, Message and Stream Semantics
    • Protocols Compared: What Is a Protocol?
    • Drills: What Is a Protocol?
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 0: What Is a Protocol?
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 0: What Is a Protocol?
  4. A Protocol Is a Contract

Part 0 · What Is a Protocol? · Foundation (F)

A Protocol Is a Contract

A protocol is the set of promises two independently built blocks may rely on, each one checkable on the wires between them.

On this page

    Note

    Level: Foundation (F) · Answers: What is a protocol, really?

    Before this: Part 0 overview

    Two teams build the two sides of an interface from the same two-page document. Each side passes its own tests. On the first integrated run the system hangs within a microsecond. The sending team assumed the receiver could always accept a word; the receiving team assumed the sender would wait until asked. Both read the document carefully, and both were right about what it said. It never said who waits for whom.

    What exactly must a protocol pin down so that two blocks, built separately, work the first time they meet?

    The obvious answer, and why it breaks

    The first answer most people give is a port list: signal names, widths and directions. It is necessary, but it promises almost nothing. Take the smallest possible interface: a valid signal and an 8-bit data bus from a sender to a receiver. The port list does not tell you:

    • whether data is meaningful in the same cycle valid is high, or one cycle later;

    • whether valid may stay high for two cycles to send the same word twice, or two different words;

    • whether the receiver can refuse a word, and how the sender would find out;

    • what valid is while the sender is in reset.

    Two careful engineers can answer each question differently, and both designs will match the port list exactly. The failure only appears when the two sides meet.

    The idea: a protocol is a set of promises

    A protocol is a contract between two parties that cannot see inside each other. Each side can observe only the wires. So everything one side relies on must be something the other side promises, and every promise must be checkable on the wires: if you cannot tell from the signals whether a promise was kept, it is not a promise, it is a hope.

    Think of two companies that ship goods to each other without ever visiting each other's factories. The delivery contract says what arrives, in what packaging, at which dock, and what happens when a delivery is refused. Neither cares how the other runs its factory, and the contract must not depend on it.

    Most promises fall into four kinds:

    Kind of promise

    What it says

    Example

    Meaning

    What each value stands for

    valid = 1 means the bus carries a real word

    Timing

    When a value counts, and how long it must stay

    data is captured on the rising edge where valid is high

    Sequence

    What may follow what

    a new word may not replace one that has not been taken

    Exceptional cases

    Reset, errors, power-down

    during reset the sender drives valid = 0

    The canonical picture of this part: two hidden implementations meet only at the contract.

    Worked example: the full contract of a tiny interface

    Here is a complete contract for the valid/data interface above, extended with a ready signal so the receiver can refuse. Read each row as "who promises what, and how would you check it on a waveform".

    #

    Promise

    Made by

    How to check it on the wires

    1

    data is meaningful only in cycles where valid is high

    Sender

    Ignore data when valid is low; nothing to check

    2

    A word moves on a rising edge where valid and ready are both high

    Both

    Count edges with both high; that is the number of words

    3

    Once valid is high, valid and data stay unchanged until the word moves

    Sender

    If valid and not ready at an edge, the next cycle has the same valid and data

    4

    valid does not wait for ready

    Sender

    Not checkable on one waveform; checked by design review or formal proof (lesson 3.4)

    5

    While rst_n is low the sender drives valid = 0

    Sender

    valid is 0 in every cycle of reset

    6

    The receiver eventually raises ready

    Receiver

    A liveness promise: checked with a bound ("within 100 cycles") or by a formal tool

    Notice what the contract does not say: how deep the receiver's buffer is, how many cycles it takes to process a word, how the sender generates data. Those are left open on purpose, so either side can be redesigned without touching the other. A good contract is as small as it can be while still letting the two sides work together.

    Promises like row 3 translate directly into assertions, the executable form of a contract. Part 15 builds a full set; here is the shape of one:

    design.sv
    // snippet: promise 3 as a SystemVerilog assertion
    assert property (@(posedge clk) disable iff (!rst_n)
      valid && !ready |=> valid && $stable(data));

    The assertion reads almost like the promise: if valid is high and ready is low at an edge, then at the next edge valid is still high and data has not changed.

    The design space: what to promise and what to leave open

    Decision

    Promise it

    Leave it open

    Latency

    When the other side must plan around it (fixed-latency memories)

    Usually: let each implementation take as long as it needs, and use a handshake

    Buffering

    Only when the other side's correctness depends on it (credits, Part 4)

    Otherwise: it is an internal choice

    Order of results

    Always state it, even if the answer is "no order" (Part 7)

    Never leave it implicit

    Reset and error behaviour

    Always

    Never: it is where most integration bugs live

    Optional features

    As explicit, negotiable options (Part 11)

    Silently optional features become incompatibilities

    How real protocols chose

    Protocol

    A promise it makes on the wires

    APB

    A transfer has a one-cycle setup phase and then an access phase that lasts until the completer drives PREADY high; address, control and write data stay constant through the access phase.

    AXI

    A source must not wait for READY before asserting VALID, and once VALID is high it stays high, with the payload stable, until the transfer happens.

    I2C

    Data on SDA is stable while SCL is high; a change of SDA while SCL is high is not data but a START or a STOP condition.

    Each of these is a promise about meaning and timing that you can check by looking at a waveform. None of them says anything about how the blocks are built inside.

    What breaks

    Back to the opening hang (illustrative). The document described valid, data and ready, but not reset. The sender came out of reset one cycle later than the receiver and, during that cycle, its valid output was not yet driven to a known value. In simulation it was X; in the receiver's model, X was treated as "maybe", and the receiver's state machine took a branch that waited forever for a word that never existed. Each team's tests started both sides out of reset at the same time, so neither saw it.

    The fix was one row in the contract, promise 5 above, plus an assertion for it. The lesson is general: integration bugs live in the cases the contract forgot, most often reset, errors, back-pressure and the first and last transfer.

    Why this matters

    Senior lens. When you review an interface specification, walk every signal through four questions: what does each value mean, when does it count, what may follow what, and what happens in reset and on error. Then ask what the contract deliberately leaves open, and whether either side secretly depends on it. Implicit dependencies, such as a receiver that happens to accept within two cycles and a sender that quietly relies on it, are the ones that break when a block is reused in a new system.

    Interview

    Asked: "What is a protocol, beyond a list of signals?"

    Weak answer: It's the set of signals and their timing diagram.

    Strong answer: It's a contract between two independently designed blocks: promises about what each value means, when it counts, what sequences are allowed, and what happens in reset and on errors. Every promise has to be checkable at the interface, because neither side can see inside the other. It should also leave internal choices, like latency and buffering, open so either side can change.

    Follow-ups: (1) Give one promise from a protocol you know that a port list does not capture. (2) How would you turn a contract into something a simulator can check?

    Check yourself A specification says "the receiver processes each word within two cycles". Is this a good promise to put in an interface contract?

    Only if the sender genuinely needs it, for example a sender with no back-pressure input that must know the receiver keeps up. Otherwise it constrains every future receiver for no benefit. The usual better choice is a handshake (ready), which lets the receiver take as long as it needs.

    Check yourself Which of these can be checked on a single waveform: (a) data is stable while valid is high and ready is low; (b) the receiver eventually raises ready?

    (a) can: look at every stalled cycle. (b) cannot on a finite trace, because "eventually" has no deadline; you check it with a bound ("within N cycles") in simulation or with a formal tool.

    Check yourself Why do integration bugs cluster around reset?

    Reset is the case contracts most often forget, and the two sides of an interface rarely leave reset at exactly the same time. Whatever a side drives in the gap is unspecified unless the contract says so.

    Key takeaways

    • A protocol is a contract: promises about meaning, timing, sequence and exceptional cases that two hidden implementations rely on.

    • Every promise must be checkable on the wires; if you cannot see it on a waveform or prove it, it is not a promise.

    • Good contracts state reset and error behaviour explicitly and leave internal choices, such as latency and buffering, open.

    Keep going

    Next: Lesson 0.2, Roles and Agents. Who starts a transaction, who answers it, and why one block can play several roles at once.

    Related topics

    • Part 0 overview: What Is a Protocol?

    • Lesson 3.3: The Valid/Ready Handshake

    Previous

    5 / 234

    Next: Roles and Agents

    Up next

    Up nextRoles and Agents

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Signals, Meaning and Timing
    • Reading Timing Diagrams
    • Protocols as State Machines