A computer boots with a network card, two drives and a USB hub with a keyboard on it. Nobody told the operating system any of this. Before the first driver loads, the system has to discover what is attached, find out what each thing is, and give each one an address it can be reached at. A working link is only a wire; enumeration turns it into a known device.
How does a host find out what is attached and how to talk to it?
The obvious answer is a fixed table: device X at address Y, written into firmware. That works for a fixed embedded system and nothing else. Any system with slots, ports or options has an unknown set of devices, and two identical devices cannot both have the same fixed address. Discovery has to work with no prior knowledge.
A default way to reach an unconfigured device: a default address (USB address 0), or a position in a hierarchy (PCI bus, device and function numbers derived from where it sits).
Self-description: every device answers a few standard questions in a standard format: who made it, what kind of device it is, what resources it needs.
Assignment: the host gives each device its unique address or its place in the address map, and records it.
Configuration: the host enables the device's features and hands it to a driver chosen from its self-description.
Approach | How | Example |
|---|---|---|
Hierarchical (position-based) | Walk the tree from the root: each bridge or hub is given a number range, each device found below it is numbered in turn | PCI Express bus numbers assigned depth-first; memory windows sized from the device's BARs |
Default address, then assign | Talk to one new device at a time at a shared default address, then move it to a unique address | USB: address 0, then SET_ADDRESS |
Memory-mapped devices also need a place in the address map. PCI Express finds how much each device needs with a simple trick: software writes all 1s to a Base Address Register and reads it back; the bits that stay 0 reveal the size of the window, which software then places in the map.
Protocol | Enumeration |
|---|---|
PCI Express | Each function has configuration space (256 bytes, extended to 4 KB) with vendor and device IDs, class code and BARs; software scans depth-first, assigning bus numbers to bridges and sizing each BAR. |
USB | A new device answers at address 0; the host reads its descriptors, assigns a unique address with SET_ADDRESS and selects a configuration. |
On-chip buses | Usually a fixed map in the design, sometimes with a discovery ROM listing the blocks present |
A device (illustrative) reported a BAR size of 1 MB but decoded only the bottom 64 KB of it, mirroring its registers through the rest. Enumeration placed another device's window inside the mirrored region on one machine, and writes to one device occasionally landed in the other. The fix was to report the size the device actually decodes, and to decode exactly that.
The host enumerates one new device at a time and moves each to a unique address before talking to the next.
64 KB: the low 16 bits are 0, so the window is 2^16 bytes.
Enumeration discovers what is attached with no prior knowledge.
It needs a default way to reach a new device, self-describing answers, and address assignment.
Report exactly the resources a device decodes; enumeration bugs appear only on some machines.