Skip to content
VLSI Mentor

USB · Module 1

The Parallel Port

Why presenting eight data lines at once forces an explicit data/strobe/acknowledge handshake, what that costs in timing discipline, a synthesizable teaching FSM that implements it with the assertions that protect it, and why an interface shaped around one peripheral's operational model cannot generalise.

Chapter 1.3 took the serial column of the landscape apart and found an interface that draws its boundary at the wire: it moves bytes between two endpoints and declines to say what they mean. This chapter takes the column beside it, and the contrast is deliberate — the parallel printer port answers almost every question differently.

It presents several data bits simultaneously rather than one after another. It was shaped around a particular peripheral's operational model rather than declining to define meaning. And where RS-232 asked both ends to agree on timing in advance, the parallel port carries its coordination on the wires themselves, in a handshake of control signals that says here is data and I have taken it.

That handshake is the reason this chapter can go deeper than 1.3 into hardware. A signalling convention agreed out of band leaves little for a controller to do beyond serialise; an explicit handshake is a state machine with real timing obligations, and those obligations are where RTL designers and verification engineers actually live. So this chapter builds the controller, states what it does not model, and then breaks it on purpose.

1. Why Parallel at All

Start with the design pressure, because "several wires instead of one" is a means rather than a motive.

A printer wants bytes, in order, as fast as the mechanism can consume them, over a short cable to a device sitting on the same desk. Under exactly those conditions, presenting eight data bits at once on eight lines is an entirely reasonable answer: the interface delivers a whole character per transfer without any serialisation logic at either end, and the receiving device can latch the byte directly.

Compare that with what §3 of Chapter 1.3 required of a serial link. There, the receiver had to reconstruct timing from a start transition and prior agreement about bit period. Here, there is nothing to reconstruct — the bits are simply present, and a separate signal says when to look at them. The parallel interface trades wires for the elimination of timing recovery, which on a short cable to a nearby device is a good trade.

The cost arrives with distance and rate. Eight lines carrying bits that must be valid simultaneously at the far end means the interface's correctness depends on those lines staying adequately aligned with each other. Every source of skew — differing trace and wire lengths, differing driver and receiver characteristics, differing loading — eats into the window in which all eight are stable together. Lengthen the cable or shorten the transfer period and that window closes.

2. The Interface as an Engineering Model

Strip away the connector and the parallel port is three groups of signals moving in defined directions.

Data lines, driven by the host toward the peripheral in the interface's original forward direction, carrying one byte per transfer.

Control signals, also driven by the host, of which the important one here is the strobe — the signal that says the data lines are valid now, take them. The others manage the peripheral's broader state and are not the subject of this chapter.

Status signals, driven by the peripheral back toward the host. Two matter for the transfer itself: an acknowledge, a pulse indicating the byte has been accepted, and a busy indication showing the peripheral is not presently able to take another. Others report conditions such as being out of paper — and the existence of those signals is itself informative, because it shows an interface whose vocabulary was drawn from one device class's operational concerns.

A host parallel port controller connects to a peripheral through three groups of signals. The host drives eight data lines and a strobe control signal toward the peripheral. The peripheral drives acknowledge and busy status signals back toward the host. The host-side port logic holds a data register and a handshake state machine; the peripheral latches the data when the strobe asserts and answers with an acknowledge pulse. The data and strobe must satisfy a setup and hold relationship at the peripheral's latch.Host port logicdata register + handshake FSMData linesone byte, presented togetherStrobedata is valid nowAcknowledge / busytaken · not readyPeripherallatches on strobe, answers12
Figure 1 — three signal groups, two directions, and one handshake between them.

Two properties of Figure 1 deserve naming now, because the rest of the chapter rests on them.

First, the transfer is a closed loop, not a broadcast. The host does not simply place bytes on the wires at a rate it chooses; it waits to be told each one was taken. That is genuine flow control built into the interface rather than bolted on by agreement, and it is what lets a fast host drive a mechanically slow device without losing data.

Second, the strobe and the data are a timing pair. The peripheral captures the data lines in response to the strobe, so the data must already be stable when the strobe asserts and must remain stable until it has been captured. Setup and hold, in other words — the same relationship a flip-flop imposes on its input relative to its clock, here stretched across a cable between two boxes.

3. A Standard That Arrived Afterwards

A short historical note, scoped carefully because this area is often taught loosely.

The forward-direction handshake described above came from a manufacturer's printer interface and spread by de-facto adoption rather than by standardisation — implementations converged because everyone copied a working arrangement. Formal standardisation came later, with IEEE 1284, which specified the interface properly and added defined bidirectional modes, so that a peripheral could return data at more than the trickle the original status lines allowed.

Two things follow that matter more than the names.

The bidirectional modes are the interface being asked to do something it was not shaped for. The original design had a wide path outward and a handful of status bits inward, because printing is overwhelmingly a one-way activity. Devices that wanted to send data — scanners and external storage among them — needed the reverse direction to become real, and the later modes are the answer to that retrofit.

And the retrofit brought negotiation: a procedure by which the two ends establish which mode they will use, because a device supporting the extended modes must still work with a host that does not. That is worth pausing on, because it is the first time in this module that an interface has contained a mechanism for two ends to discover what the other supports. It is narrow — it negotiates a transfer mode, not a device identity — but the shape of the idea is the one Chapter 1.6 will generalise.

This chapter teaches the forward handshake only. The modes and their negotiation are named here so you know they exist and are not misled into thinking the parallel port was permanently one-directional.

4. The Timing Relationship, in a Waveform

Now make the setup-and-hold claim concrete. The waveform below is drawn from the host controller's point of view: the controller is a synchronous design, clocked internally, driving an interface whose handshake is asynchronous at the far end.

Parallel-port forward transfer — controller-side view

10 cycles
A synchronous controller performs one parallel-port forward transfer. The data lines are driven with the byte one cycle before the strobe asserts, so the data is already stable when the strobe goes low. The strobe is held low for two cycles, then released. The peripheral responds with an acknowledge pulse, after which the controller returns to idle. The controller state machine moves through IDLE, DRIVE, STROBE, WAIT-ACK and DONE.data driven — strobe still idledata driven — strobe stillidlestrobe asserts on already-stable datastrobe asserts onalready-stable dataperipheral acknowledgesperipheral acknowledgesdata unchanged — held until the next loaddata unchanged — held untilthe next loadclkstateIDLEDRIVESTROBESTROBEWAITWAITDONEIDLEIDLEIDLEport_datastale0x410x410x410x410x410x410x410x410x41strobe_nack_nbusyt0t1t2t3t4t5t6t7t8t9
Figure 2 — the controller's view of one forward transfer: data first, strobe second, acknowledge last.

Read the ordering, because the ordering is the protocol.

The data lines are driven one state before the strobe asserts. That separation is the setup margin, and it exists for a reason a simulation will not show you: the eight data lines and the strobe travel different physical paths, and if they were launched in the same instant the strobe could win the race and arrive while the data was still settling. Driving the data first buys margin against that skew.

The strobe is then held, not pulsed for an instant, because the far end needs a recognisable assertion — and because the peripheral is a separate machine with its own timing, not a flip-flop on the same clock.

The data lines are not disturbed until well after the acknowledge — in the module below they simply hold their value until a later transfer loads a new one. That is the hold obligation. A controller that changed the data lines as soon as it had de-asserted the strobe would be assuming the peripheral had already captured them, and that assumption is exactly the kind that works on the bench and fails on a longer cable.

5. The Handshake as RTL

Here is that state machine. It is small on purpose — the value is that every timing claim above becomes something you can point at.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// parallel_strobe_ack_tx
//
// Classification: SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL.
//
// MODELS: the forward-direction data / strobe / acknowledge handshake a
// Centronics-style parallel peripheral port is built around -- driving the
// data lines, asserting the strobe only after they are already stable,
// holding both until the peripheral answers, and releasing the data only
// after the acknowledge has been seen.
//
// DOES NOT MODEL: IEEE 1284 mode negotiation or any bidirectional mode
// (nibble, byte, EPP, ECP) -- see section 3; the peripheral's other status
// lines; the NUMERIC setup, hold and pulse-width obligations, which come
// from the interface specification and are deliberately absent here; pad
// electricals and cable behaviour; and error recovery when a peripheral
// never acknowledges (see ACK_TIMEOUT below, and section 7).
//
// THIS IS NOT AN IEEE 1284 IMPLEMENTATION. It is the handshake's control
// skeleton, written so section 4's ordering argument is inspectable.
// ─────────────────────────────────────────────────────────────────────────
module parallel_strobe_ack_tx #(
  parameter int DATA_W = 8,
  // Strobe assertion length, in controller clocks. A real port derives this
  // from the specification's pulse-width obligation and its own clock
  // period; 2 is a placeholder that makes the waveform legible.
  parameter int STROBE_CYCLES = 2
) (
  input  logic                clk,
  input  logic                rst_n,

  // Local (synchronous) side -- a simple valid/ready producer interface.
  input  logic                send_valid,
  input  logic [DATA_W-1:0]   send_data,
  output logic                send_ready,

  // Peripheral-facing side. port_ack_n arrives from another box on no clock
  // of ours, so it is synchronised before use -- Chapter 1.5 owns why two
  // flops, and what they do and do not guarantee.
  output logic [DATA_W-1:0]   port_data,
  output logic                port_strobe_n,
  input  logic                port_ack_n,
  output logic                busy
);

  typedef enum logic [2:0] {
    S_IDLE,     // no transfer in flight; data lines hold their last value
    S_DRIVE,    // byte driven onto the data lines; strobe still idle
    S_STROBE,   // strobe asserted over already-stable data
    S_WAIT_ACK, // strobe released; waiting for the peripheral to answer
    S_DONE      // acknowledge seen; release and return
  } state_e;

  state_e state_q, state_d;

  // Strobe duration counter. Width follows the parameter rather than being
  // a fixed guess, so raising STROBE_CYCLES does not silently truncate.
  localparam int CNT_W = (STROBE_CYCLES <= 1) ? 1 : $clog2(STROBE_CYCLES + 1);
  logic [CNT_W-1:0] cnt_q;

  logic [DATA_W-1:0] data_q;

  // ── Acknowledge synchroniser ───────────────────────────────────────────
  // Two flops against metastability, then a third to give an edge detector
  // a previous value. The acknowledge is a PULSE from a device on its own
  // timing, so we detect its falling edge rather than sampling its level:
  // a level test can miss a pulse shorter than our sampling interval.
  logic ack_n_meta_q, ack_n_sync_q, ack_n_prev_q;
  logic ack_falling;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ack_n_meta_q <= 1'b1;
      ack_n_sync_q <= 1'b1;
      ack_n_prev_q <= 1'b1;
    end else begin
      ack_n_meta_q <= port_ack_n;
      ack_n_sync_q <= ack_n_meta_q;
      ack_n_prev_q <= ack_n_sync_q;
    end
  end

  assign ack_falling = ack_n_prev_q && !ack_n_sync_q;

  // ── Sequential state ───────────────────────────────────────────────────
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q <= S_IDLE;
      cnt_q   <= '0;
      data_q  <= '0;
    end else begin
      state_q <= state_d;

      // Capture the byte at the moment the transfer is accepted, so the
      // producer may change send_data immediately afterwards.
      if (state_q == S_IDLE && send_valid) begin
        data_q <= send_data;
      end

      if (state_d == S_STROBE) begin
        cnt_q <= (state_q == S_STROBE) ? cnt_q + 1'b1 : '0;
      end else begin
        cnt_q <= '0;
      end
    end
  end

  // ── Next-state logic ───────────────────────────────────────────────────
  always_comb begin
    state_d = state_q;
    unique case (state_q)
      S_IDLE:     if (send_valid)                       state_d = S_DRIVE;
      // One full cycle with data valid and strobe idle. This state is the
      // setup margin; deleting it is the bug section 7 injects.
      S_DRIVE:                                          state_d = S_STROBE;
      S_STROBE:   if (cnt_q == CNT_W'(STROBE_CYCLES-1)) state_d = S_WAIT_ACK;
      // No timeout: a peripheral that never answers parks the FSM here
      // forever. That omission is deliberate and discussed in section 7.
      S_WAIT_ACK: if (ack_falling)                      state_d = S_DONE;
      S_DONE:                                           state_d = S_IDLE;
      default:                                          state_d = S_IDLE;
    endcase
  end

  // ── Outputs ────────────────────────────────────────────────────────────
  // port_data simply follows data_q, which is loaded only when a new
  // transfer is accepted in S_IDLE. The lines therefore hold their value
  // past DONE and into the next idle period -- which satisfies the hold
  // obligation with room to spare: they cannot move until the acknowledge
  // has been observed, because nothing reloads data_q until then.
  assign port_data     = data_q;
  assign port_strobe_n = (state_q == S_STROBE) ? 1'b0 : 1'b1;
  assign send_ready    = (state_q == S_IDLE);
  assign busy          = (state_q != S_IDLE);

endmodule

What hardware this implies. A byte-wide register holding the data lines, a five-state controller, a small counter, and three flops of synchroniser. That is genuinely all the forward handshake needs, which is worth knowing: the parallel port's host side is cheap, and its cost lives in pins rather than in logic.

Why the structure exists. S_DRIVE is not a wasted cycle — it is the setup margin of §4 expressed as a state. The separate data_q register is what lets send_ready fall in the same cycle the byte is taken, so the producer is not required to hold send_data for the whole transfer. The edge detector on the acknowledge exists because the peripheral's answer is a pulse from a machine on its own timing, and level-sampling a foreign pulse is how transfers get silently lost.

What it assumes. That the peripheral eventually acknowledges; that STROBE_CYCLES at this clock satisfies the specification's pulse-width obligation; that the busy indication is advisory rather than something the FSM must gate on; and that two synchroniser flops give acceptable metastability margin at this clock — an assumption, not a guarantee, and one Chapter 1.5 takes apart.

What a waveform would show. Exactly Figure 2. port_data changes one cycle before port_strobe_n falls, and does not change again until after ack_falling has pulsed.

What it intentionally omits. Every numeric timing obligation, the entire reverse direction, mode negotiation, and any recovery from a peripheral that never answers. Those omissions are listed in the header for a reason: a teaching module that hides its gaps teaches engineers to trust incomplete things.

6. What a Verification Engineer Owns Here

The handshake is small, which makes it an unusually clean illustration of how a DV engineer converts a protocol description into checks.

Start with the invariants — the things that must be true in every run, not merely in the ones you thought to write.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Representative assertions for parallel_strobe_ack_tx.
//
// Classification: TEACHING ASSERTIONS. They protect THIS example's control
// behaviour. They are not a parallel-port compliance suite, and they check
// no numeric timing obligation, because this example defines none.
// ─────────────────────────────────────────────────────────────────────────

// A1 -- THE CENTRAL ONE. The data lines must not move while the strobe is
// asserted. A design that violates this can still pass every directed test
// on a short cable and corrupt bytes on a long one, because the failure is
// a race between the strobe and the data, not a logic error.
property p_data_stable_while_strobe;
  @(posedge clk) disable iff (!rst_n)
    !port_strobe_n |=> $stable(port_data);
endproperty
assert property (p_data_stable_while_strobe);

// A2 -- the setup obligation as a control-path property: the data lines
// must already have been stable for at least one cycle when the strobe
// falls. This is what catches a "helpful" optimisation that merges DRIVE
// into STROBE to save a cycle.
property p_data_setup_before_strobe;
  @(posedge clk) disable iff (!rst_n)
    $fell(port_strobe_n) |-> $stable(port_data);
endproperty
assert property (p_data_setup_before_strobe);

// A3 -- the hold obligation. Data must not change in the cycle the
// acknowledge is detected or before it; releasing early is the bug that
// only a slow peripheral exposes.
property p_data_held_until_ack;
  @(posedge clk) disable iff (!rst_n)
    (state_q == S_WAIT_ACK) |=> $stable(port_data);
endproperty
assert property (p_data_held_until_ack);

// A4 -- no overlapping transfers. send_ready must be low for the whole
// time a transfer is in flight, or a producer can hand over a second byte
// while the first is still on the wires.
property p_no_overlapping_transfer;
  @(posedge clk) disable iff (!rst_n)
    busy |-> !send_ready;
endproperty
assert property (p_no_overlapping_transfer);

// A5 -- reset is a real state, not an afterthought. Whatever the FSM was
// doing, reset must leave the strobe DE-asserted; a reset that drops the
// strobe active leaves a peripheral believing a transfer is in progress.
property p_reset_releases_strobe;
  @(posedge clk)
    !rst_n |=> port_strobe_n;
endproperty
assert property (p_reset_releases_strobe);

Each of those protects a specific bug. A1 catches data moving under an asserted strobe — the defect that survives bench testing and fails in the field. A2 catches the deleted setup state. A3 catches early release. A4 catches a producer-side handshake error that would otherwise appear as intermittently corrupted output. A5 catches the reset case, which is where a surprising share of real integration bugs live, because reset is exercised least and observed last.

Now the stimulus dimensions worth randomising, because the assertions are only as good as the runs they see. The peripheral's response time is the big one: an acknowledge arriving in the next cycle and one arriving after a long delay exercise completely different paths, and a testbench whose model always answers immediately will never hold the FSM in S_WAIT_ACK long enough to matter. Beyond that: back-to-back transfers with no producer gap, transfers separated by long idle periods, reset asserted in each state of the FSM, and an acknowledge pulse of varying width including one narrower than the sampling interval.

Representative coverage dimensions — not a verification plan, but the axes worth watching:

  • acknowledge latency: immediate, short, long, and never
  • reset asserted in each of the five states, S_STROBE and S_WAIT_ACK especially
  • back-to-back transfers versus isolated ones
  • acknowledge pulse width, including the narrow case
  • send_valid de-asserting in the same cycle it is accepted
  • data patterns that toggle every line at once, which is where skew is worst

And the error injections that are actually informative: an acknowledge that never comes; an acknowledge that arrives early, while the strobe is still asserted; a doubled acknowledge for one strobe; and a peripheral that answers an acknowledge it was never strobed for.

7. Two Failures, Traced

Abstract verification advice is easy to nod at. Here are two concrete faults, each traced from the wrong idea to the symptom.

The deleted setup state. A designer notices S_DRIVE appears to do nothing — the data is already assigned, the strobe is already idle — and merges it into S_STROBE to save a cycle per byte.

Everything passes. The data register is loaded and the strobe asserts in the same cycle, and in simulation both change cleanly at the same clock edge, so no test notices. On hardware with a short cable it also works, because the skew between the data lines and the strobe is small relative to everything else. The failure appears on a longer cable, at a higher transfer rate, or with a different peripheral: occasional corrupted bytes, more frequent under load, and not reproducible on the engineer's bench. The wrong idea was that a state with no output change has no purpose; the state's entire purpose was the ordering gap between two signal groups travelling different physical paths. Assertion A2 catches it the moment it is written, which is the argument for writing it.

The level-sampled acknowledge. A designer replaces the edge detector with a level test — if (!ack_n_sync_q) state_d = S_DONE; — on the reasonable-sounding ground that a low acknowledge means the byte was taken.

This works with a peripheral whose acknowledge pulse is comfortably wider than the controller's clock period. Against a faster peripheral whose pulse is narrower, the controller samples on either side of the pulse and never sees it, and the FSM parks in S_WAIT_ACK forever. The symptom is a transfer stream that stops dead after some number of bytes, with the port apparently healthy and the peripheral reporting no error — and because there is no timeout in this design, nothing recovers. The wrong idea was treating a foreign pulse as a level. The debugging clue is the asymmetry: it works with one peripheral and hangs with another, which points at the other end's timing rather than at your own logic.

That second failure also exposes the design omission the header declared. A production controller needs a timeout so that an unanswered transfer becomes a reported error rather than a hang. It is absent here to keep the FSM readable, and naming the absence is part of the example being honest.

8. Why This Never Became a Universal Peripheral Architecture

The parallel port did more than RS-232 did. It carried its own flow control, defined a real transfer handshake, and eventually grew a negotiation mechanism. Ask, as Chapter 1.3 did, why that was still not enough.

Its vocabulary came from one device class. Status signals reporting paper conditions are not a general peripheral abstraction; they are printing, encoded into the interface. A camera or an audio device attaching here is not described by the interface's own terms, and must reinterpret them.

The wide path points the wrong way for most peripherals. The design gives many lines outward and a handful inward, which suits printing and suits almost nothing else. The later bidirectional modes are a retrofit, and a retrofit that requires negotiation to remain compatible.

It still does not say what is attached. Mode negotiation establishes how to talk, not who is talking. The identity gap of Chapters 1.1 and 1.3 is untouched: a host knows it has a parallel peripheral, not what kind.

Its physical form resists scaling. Correctness depends on many lines being valid together, so the pin count is the interface, and the skew budget tightens as rate or cable length grows. Ports were consequently few — commonly one — and adding devices meant adding ports or switching between them.

Attachment and power sit outside the model, exactly as Chapter 1.2's comparison table recorded.

And say the fair thing, as with RS-232: this is not a list of defects. An interface that moves bytes to a nearby printer with genuine flow control and cheap host logic is a good interface for that job, and it held that job for a long time. What it cannot do is become the attachment model for peripheral classes that did not exist when its vocabulary was chosen — because the vocabulary was chosen.

9. Why This Matters to a Semiconductor Engineer

Three transferable things came out of this chapter, and none of them is about printers.

A handshake is a contract with an ordering, and ordering is verifiable. Assertions A1 to A3 are not parallel-port-specific ideas; they are the generic shape of data valid before qualifier, held until accepted, which recurs in every valid/ready interface you will ever integrate. Recognising that shape lets you reuse the checks.

An interface crossing between boxes is a clock-domain boundary whether or not anyone says so. The acknowledge arrives on no clock of yours. That fact forced a synchroniser into a design that otherwise looks entirely local, and forgetting it is among the most common integration bugs in FPGA work — which is exactly why Chapter 1.5 builds the argument properly.

Timing failures do not look like logic failures. The deleted setup state produced no simulation failure, no error report, and no reproducible bench symptom — only occasional corruption on someone else's hardware. Learning to recognise that signature is worth more than memorising any interface.

10. Common Misconceptions

11. Reason It Through

A controller drives the data lines and asserts the strobe in the same clock cycle. Simulation passes every test. The board works on the engineer's desk. The customer reports occasional corrupted bytes on a longer cable, more often at higher throughput.

What is the fault? The setup margin has been removed. Data and strobe are launched together, so they race across the cable, and where the strobe wins the peripheral latches data that has not finished settling.

Why did simulation pass? Because an RTL simulation of the controller has no model of the cable. Both signals change at the same edge and every observed relationship is exactly as intended — the defect lives in a physical domain the testbench does not represent.

Why is it intermittent and load-dependent? Because it is a race. The margin lost is small, so corruption needs skew and settling time to align unfavourably, which longer cables and shorter transfer periods make more likely.

What would have caught it? Assertion A2 — $fell(port_strobe_n) |-> $stable(port_data) — which fails the moment the two are launched together, in the same simulation that otherwise passes. This is the argument for control-path assertions generally: they encode why a structure exists, so that removing the structure fails loudly instead of silently.

What is the general principle? A state whose only job is to create an ordering gap looks redundant in every view except the one that matters. Before deleting a state that appears to do nothing, ask what relationship it is protecting.

12. Understanding Check

13. Summary

The parallel port replaces timing agreed in advance with timing asserted on the wire. Eight data lines carry a byte with no serialisation and no timing recovery, and a strobe says when they are valid — which turns the interface into a handshake with setup and hold obligations stretched across a cable, and turns the host side into a small state machine with real timing responsibilities.

That handshake closes a loop. The peripheral's acknowledge means taken, giving the interface genuine flow control and letting a fast host drive a slow mechanism safely. Formal standardisation and real bidirectional modes arrived later, with IEEE 1284, and brought a negotiation procedure — the first mechanism in this module by which two ends discover anything about each other, though it negotiates a transfer mode and not a device identity.

The engineering content generalises. Data valid before the qualifier, held until accepted is the shape of every valid/ready interface you will integrate, and it is verifiable with a handful of assertions that encode why each state exists. An interface crossing between boxes is a clock-domain boundary whether or not anyone labels it one. And timing failures announce themselves differently from logic failures — no simulation failure, no clean reproduction, intermittent under physical stress — which is a signature worth recognising directly.

What it could not become is a general peripheral architecture, for reasons that are structural rather than remediable: a signal vocabulary drawn from printing, a wide path pointing the wrong way for most devices, no statement of what is attached, a pin count that is the interface, and attachment and power outside the model. Well matched to its job, and shaped by that job too tightly to serve another.

14. What Comes Next

Two stacks examined, and both put the host in charge of timing: RS-232's host agrees the bit period in advance, and the parallel port's host asserts the strobe. Chapter 1.5 takes the dedicated input ports, where that assumption is inverted — the device supplies the clock, and the host must receive on someone else's timing.

That inversion is what makes PS/2 the most instructive of the three for a hardware engineer. An input arriving on a foreign clock cannot simply be sampled; it has to be brought into the receiver's domain safely first, and the synchroniser this chapter used in passing becomes the chapter's own subject. Chapter 1.6 then gathers the evidence from all three and derives what a replacement architecture would have to provide.

Browse the full path on the USB tutorials index.

Continue learning

Standards & specifications

Governing standard
USB-IF (Universal Serial Bus Specification)(opens USB Implementers Forum (USB-IF) in a new tab)

Defines the USB bus — its electrical signalling, connectors, packet and transaction model, device framework and the descriptors a device must expose — together with the device-class specifications layered on it. It does not define host-controller register interfaces (xHCI and EHCI are separate documents) nor any operating system's driver architecture.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the USB curriculum.