I/O e periferiche: dal bus al mondo esterno
CPU e RAM da sole non parlano con nessuno. Scopriamo l'unità di I/O: il ponte che traduce, sincronizza e coordina tastiere, dischi, GPU e rete con il bus di sistema.
1. L'unità di I/O — il confine tra CPU e mondo esterno
CPU e memoria centrale, da sole, non possono fare nulla di utile: hanno bisogno di ricevere dati dall'esterno e di restituire risultati. L'unità di Input/Output è il sottosistema che rende possibile questa comunicazione — è il confine fisico e logico tra il sistema di elaborazione e l'ambiente esterno.
Unità di I/O: sottosistema che gestisce traduzione dei segnali, sincronizzazione delle velocità, coordinamento dei trasferimenti e indirizzamento delle periferiche tra bus di sistema e mondo esterno.
Traduzione dei segnali
Converte il formato elettrico/logico della periferica nel formato del bus di sistema e viceversa. Una tastiera manda scan code, il bus vuole byte su data bus.
Sincronizzazione velocità
La CPU opera a GHz, una tastiera a pochi byte/s, un HDD a MB/s. L'I/O colma il divario con buffer e logiche di attesa.
Gestione trasferimenti
Coordina quando e come i dati si spostano tra periferica, bus e memoria: chi inizia, quanto dura, come si segnala la fine.
Indirizzamento periferiche
Ogni dispositivo ha un indirizzo logico unico per essere identificato dalla CPU, come un numero di casa sul bus.
Senza I/O, la CPU non saprebbe come parlare con una tastiera, un disco o una scheda di rete — dispositivi con velocità, protocolli e formati completamente diversi tra loro e dal bus di sistema.
All'interrogazione chiedono sempre: "perché non colleghiamo direttamente la periferica al bus?". Risposta in 10 secondi: perché è troppo lenta e parla un linguaggio diverso. Ripeti questa frase e poi aggiungi le 4 funzioni: traduzione, sincronizzazione, trasferimento, indirizzamento.
2. Le interfacce di I/O — il modulo intermedio indispensabile
Una periferica non può essere collegata direttamente al bus di sistema. Perché? Tre motivi fondamentali: è troppo lenta, usa livelli elettrici diversi, parla un protocollo proprietario. L'interfaccia di I/O (detta anche controller o modulo di I/O) fa da adattatore universale.
Interfaccia di I/O: dispositivo hardware che collega la periferica al bus; espone alla CPU registri standard e gestisce lato periferica il protocollo specifico.
Velocità diverse
CPU GHz vs tastiera 10 B/s. L'interfaccia inserisce un buffer elastico: la CPU scrive e se ne va, la periferica svuota con calma.
Formato diverso
Il bus usa 32/64 bit paralleli, la periferica magari seriale. L'interfaccia fa conversione parallelo/seriale e codifica.
Protocollo diverso
Ogni periferica ha comandi propri. L'interfaccia traduce comandi standard (READ/WRITE) in comandi specifici dell'hardware.
Controller SATA del nostro SSD NVMe: lato bus parla PCIe 4x a 8 GB/s, lato periferica gestisce comandi NVMe (queue, TRIM). Stesso ruolo per la scheda video: lato bus PCIe, lato periferica HDMI/DisplayPort con segnali video TMDS. Senza controller dovremmo riprogettare la CPU per ogni monitor!
3. I registri e il buffer: come parla la CPU con l'interfaccia
La CPU non vede la periferica, vede solo tre tipi di registri nell'interfaccia, mappati su indirizzi.
| Registro | Cosa contiene | Direzione | Esempio |
|---|---|---|---|
| DATO (Data) | Il byte/parola in transito | CPU ↔ Interfaccia ↔ Periferica | Carattere digitato, pixel da visualizzare |
| STATO (Status) | Flag: pronta? occupata? errore? | Periferica → CPU (sola lettura) | Bit READY=1 = dati disponibili |
| CONTROLLO (Control) | Comandi dalla CPU | CPU → Periferica (sola scrittura) | START, STOP, RESET, INT_ENABLE |
Memorizza con la frase "DSC - Dato Stato Controllo". All'esame disegna sempre tre quadratini etichettati. Se non menzioni il registro di stato, l'interrogazione è insufficiente: è lui che dice alla CPU se può leggere/scrivere.
Il buffer è la chiave per la sincronizzazione: piccola memoria FIFO (First In First Out) dentro l'interfaccia. La CPU deposita velocemente 4KB nel buffer della stampante e torna a eseguire codice, mentre la periferica li stampa in 2 secondi. Senza buffer, la CPU dovrebbe aspettare fisicamente la periferica → spreco enorme.
4. Indirizzamento: memory-mapped vs I/O isolato
Come assegna la CPU un indirizzo a una periferica? Ci sono due scuole di pensiero, entrambe presenti nei processori che usi.
| Caratteristica | Memory-Mapped I/O (MMIO) | I/O Isolato / Port-Mapped (x86) |
|---|---|---|
| Spazio indirizzi | Unico: RAM e periferiche condividono lo stesso spazio (es. 0x4000... riservato a I/O) | Doppio: spazio RAM separato dallo spazio porte I/O (0-65535) |
| Istruzioni | Stesse di memoria: LOAD, STORE, MOV | Istruzioni dedicate: IN, OUT (x86) |
| Protezione | Si usa MMU / paging come per la RAM | Serve meccanismo separato, meno flessibile |
| Decodifica | Semplice, meno hardware | Più complessa, due decodifiche |
| Dove si usa | ARM, RISC-V, moderni SoC, GPU, microcontrollori | x86 Intel storico, ancora presente per compatibilità |
Su Arduino (AVR è memory-mapped) scrivi PORTB = 0xFF per accendere i LED: stai scrivendo in una locazione RAM che è in realtà il registro dato del controller. Su PC x86, per leggere il CMOS clock, il sistema operativo usa out 0x70, al / in al, 0x71: sta usando porte I/O isolate, non MOV. Due mondi, stesso concetto.
Memory-mapped NON significa che la periferica è in RAM! Significa solo che condivide lo stesso spazio numerico degli indirizzi. Parte di quegli indirizzi, quando li usi, invece di andare in RAM vanno dirottati sull'interfaccia I/O. È un trucco di numerazione, non fisico.
5. Le 3 modalità di trasferimento: polling, interrupt e DMA
Una volta mappata l'interfaccia, come fa la CPU a sapere quando trasferire? Tre strategie, in ordine di efficienza crescente.
5.1 Polling (attesa attiva)
La CPU interroga in loop il registro di stato: "Sei pronta? E ora? E ora?". Semplicissimo, ma brucia cicli CPU anche se la periferica risponde dopo 10 ms.
Per periferiche velocissime dove l'attesa è brevissima (es. sensore interno) o nei bootloader dove gli interrupt non sono ancora configurati. In Arduino while(!Serial.available()) è polling.
5.2 Interrupt (interruzione)
La CPU si fa gli affari suoi ed esegue altro codice. Quando la periferica è pronta, alza una linea di interrupt, la CPU salva contesto (PC, registri, flag), salta a una ISR - Interrupt Service Routine tramite vettore di interrupt, serve la periferica e riprende da dove aveva lasciato.
Interrupt: segnale asincrono dalla periferica che sospende il programma corrente, salva il contesto, esegue la ISR e ripristina. La CPU non spreca attese.
5.3 DMA - Direct Memory Access
Per trasferimenti di grandi blocchi (disco → RAM, RAM → scheda video), coinvolgere la CPU byte per byte è insensato. Entra il controller DMA: la CPU gli dice "trasferisci N byte da indirizzo periferica P a indirizzo RAM R" e lo lascia lavorare prendendo il controllo del bus. Il DMA trasferisce tutto direttamente, senza passare per la CPU, e alla fine genera un solo interrupt di completamento.
Schema da disegnare in verifica: CPU → programma DMA (sorgente, destinazione, contatore) → DMA prende bus (bus request / bus grant) → trasferimento periferica ↔ RAM → interrupt finale → CPU riprende. Se scrivi solo "il DMA va più veloce", prendi mezzo voto. Devi menzionare il bus grant e l'unico interrupt finale.
| Modalità | Costo CPU | Latenza | Uso tipico |
|---|---|---|---|
| Polling | 100% in attesa | Bassissima se periferica pronta | Boot, sensori veloci, debug |
| Interrupt | Quasi 0%, solo ISR | Media (salvataggio contesto) | Tastiera, mouse, rete a pacchetti |
| DMA | Solo setup + 1 interrupt finale | Alta all'inizio, poi throughput max | Disco, SSD, GPU, scheda di rete, audio |
Dire che "gli interrupt non usano la CPU". Falso: usano la CPU per eseguire la ISR, ma solo quando serve. Il polling invece la usa sempre per controllare. Il DMA è l'unico che trasferisce veramente senza CPU (a parte il setup).
6. Classificazione delle periferiche: chi fa cosa
Non tutte le periferiche sono uguali. Classificarle ti aiuta a capire che tipo di interfaccia e modalità ti serve.
⌨️ Input
Tastiera, mouse, scanner, microfono, webcam, sensori temperatura. Solo verso CPU. Critico il tempo di risposta, spesso gestito a interrupt.
🖥️ Output
Monitor, stampante, casse, proiettore, attuatori (motori, relay, LED). Solo da CPU verso esterno. Spesso con buffer grande.
💾 Memorizzazione
HDD, SSD NVMe/SATA, chiavetta USB, scheda SD, nastro. Persistente, bidirezionale, blocchi grandi → regno del DMA.
7. Interne vs esterne: bus dedicati e porte standard
PCIe, SATA, M.2
Collegate direttamente su bus interni ad alta velocità, senza overhead di protocollo esterno. Massima banda, latenza minima. Es: SSD NVMe, GPU dedicata, scheda Wi-Fi interna, RAM. Spesso usano DMA e MMIO.
USB, HDMI, Thunderbolt, Ethernet
Connesse tramite porte standard universali e hot-plug. Velocità variabile, più overhead ma massima compatibilità. Es: chiavette, monitor, stampanti, hub. USB è il re dell'interfaccia esterna: 1 cavo, alimentazione + dati.
Domanda trabocchetto: "SSD interno vs chiavetta USB: perché il primo è 10 volte più veloce se entrambi sono Flash?". Risposta: non è la memoria, è il bus. NVMe su PCIe 4x = 7 GB/s, USB 3.0 = 400 MB/s reali. Stessa tecnologia, interfaccia diversa. Ricordalo.
8. Verifica operativa
🔬 Metti alla prova il tuo I/O
- Perché una tastiera non può essere collegata direttamente al data bus? Cita almeno 2 motivi.
- Disegna e spiega i 3 registri di un'interfaccia: cosa contiene ciascuno e chi lo legge/scrive.
- Hai un sensore di temperatura che invia 1 byte ogni 10 secondi. Meglio polling, interrupt o DMA? Motiva.
- Devi copiare 2 GB da SSD a RAM. Perché usare DMA invece di interrupt byte-per-byte? Quanti interrupt risparmi?
- Differenza pratica: su un microcontrollore ARM scrivi
*(0x40001000)= 'A'per mandare una lettera su UART. È memory-mapped o I/O isolato? Perché?
1) Troppo lenta (GHz vs Hz) e protocollo diverso (scan code vs bus parallelo). Serve buffer e traduzione → interfaccia.
2) DATA = dato in transito (R/W), STATUS = flag READY/BUSY/ERR (R per CPU), CONTROL = START/STOP/INT_ENABLE (W per CPU).
3) Interrupt: dato raro, polling sprecherebbe CPU per 10 s in attesa. DMA eccessivo per 1 byte.
4) Con interrupt: 2GB = 2 miliardi di interrupt + ISR. Con DMA: setup + 1 interrupt finale = 1. Risparmio enorme, bus libero.
5) Memory-mapped: usi istruzione STORE normale su indirizzo che sembra RAM, non IN/OUT. Tipico ARM/RISC-V.
Confondere buffer con cache. Buffer = nell'interfaccia I/O, compensa velocità periferica vs CPU, FIFO piccola. Cache = tra CPU e RAM, velocizza accessi a RAM. Ruoli diversi, posizioni diverse, anche se entrambi "memorizzano temporaneamente".
9. Riepilogo e concetti chiave
📚 Prossimo passo - Le componenti di un PC e la fase di avvio
Ora che sai come CPU parla con periferiche, apriamo davvero il case: scheda madre, chipset, alimentazione, BIOS/UEFI e la sequenza di boot che carica il sistema operativo usando proprio le modalità I/O appena viste. Porta schema periferiche interne/esterne.