Introduzione alla interazione SW/HW e la modalità di protezione per d’accesso alle periferiche
L’interazione tra software e hardware si fonda su livelli gerarchici di astrazione :
- Alla base troviamo i device controller (o interfacce di I/O), circuiti digitali che espongono registri di stato, controllo e dati.
- Al livello superiore opera il software, specificamente i driver, incaricati di astrarre queste risorse hardware per renderle fruibili al Sistema Operativo o all’applicazione.
Il modello di accesso alle periferiche è strettamente legato all’architettura e alla gestione della memoria (MMU nei processori general-purpose, MPU nei microcontrollori):
- PC / Architetture High-Level (es. x86): L’accesso diretto all’hardware dallo spazio utente è proibito per garantire protezione e stabilità. L’interazione avviene tramite Syscall che invocano driver in esecuzione in Kernel Mode.
- Microcontrollori / Embedded (es. STM32, MIPS32 bare-metal): Il firmware ha accesso diretto allo spazio di indirizzamento delle periferiche (Memory-Mapped I/O). Lo sviluppatore può agire direttamente sui registri o utilizzare librerie di astrazione come HAL (Hardware Abstraction Layer).
- Simulatori Didattici (es. MARS): Utilizzano un modello ibrido semplificato di syscall per emulare l’I/O, utile a fini didattici ma strutturalmente diverso dall’hardware reale.
1. Funzionamento di un driver
Il driver è il modulo software che governa l’interfaccia di I/O (Device Controller). Le sue responsabilità principali sono:
- Configurazione e Controllo: Invia comandi al controller e ne verifica lo stato (es. polling del bit
ReadyoDone). - Trasferimento Dati: Gestisce il flusso di dati tra il buffer del controller (Registro Dati) e la memoria principale del sistema.
Flusso operativo tipico (Read/Write):
- Inizializzazione: La CPU (tramite il driver) istruisce il controller sull’operazione da eseguire (es. Read/Write) scrivendo nei registri di controllo e indirizzando la porta corretta.
- Handshaking & Esecuzione: Il controller gestisce la transazione fisica con la periferica esterna. In questa fase, il driver può attendere attivamente (polling) o sospendersi in attesa di un interrupt.
- Data Transfer: Una volta che il dato è disponibile (in lettura) o trasmesso (in scrittura), il driver completa il trasferimento da o verso la memoria centrale
2. Device controller & Driver software
Questa sezione analizza la distinzione tra l’interfaccia hardware della periferica (Device Controller) e il software di gestione (Driver), confrontando le implementazioni su architetture Embedded (ARM/MIPS) e PC (x86).
2.1 Definizioni Fondamentali
- Device Controller (HW): È il circuito digitale che funge da interfaccia tra il bus di sistema e il dispositivo fisico (es. il motore di un disco, il transceiver UART).
- Interfaccia Lato CPU (Front-end): Espone un banco di registri mappati in memoria (MMIO – Memory Mapped I/O) o, in sistemi legacy x86, nello spazio di I/O (Port I/O). I registri standard includono:
- Control Register: Per inviare comandi e configurazioni (es. baud rate, abilitazione interrupt).
- Status Register: Per leggere lo stato (es. Ready, Busy, Error).
- Data Register (FIFO/Buffer): Per lo scambio dei dati (RX/TX).
- Interfaccia Lato Bus: Gestisce i segnali di arbitraggio, le linee di interruzione (IRQ) e, se supportato, agisce come Bus Master per operazioni DMA.
- Interfaccia Lato CPU (Front-end): Espone un banco di registri mappati in memoria (MMIO – Memory Mapped I/O) o, in sistemi legacy x86, nello spazio di I/O (Port I/O). I registri standard includono:
- Driver Software (SW): È il codice (parte del Kernel, HAL o BSP) responsabile dell’astrazione dell’hardware.
- Programma i registri del controller per eseguire operazioni di I/O.
- Gestisce la sincronizzazione (Polling, Interrupt, DMA).
- Offre API di alto livello all’applicazione (es.
Serial_Write()), nascondendo la complessità dei registri hardware.
2.2 Analisi Comparata per Architettura : MCU, PC , MARS/MIPS
Di seguito il confronto tecnico tra le modalità di accesso e gestione nelle principali architetture.
A. Microcontrollori (STM32 / PIC32) – Dominio Embedded
In questo contesto, il software (Bare-metal o RTOS) ha spesso accesso diretto allo spazio di indirizzamento fisico.
- STM32 (ARM Cortex-M):
- Controller: Espone registri mappati in uno spazio di indirizzamento piatto a 32-bit (es.
USART1base address0x40013800). Include registri comeCR1(Control),SR(Status),DR(Data). - Driver (HAL/LL): Accede ai registri dereferenziando puntatori a struttura (es.
huart->Instance->DR = data). Configura il NVIC (Nested Vectored Interrupt Controller) per gestire le priorità delle ISR e configura i canali DMA per il trasferimento diretto memoria-periferica. - Protezione: Opzionale tramite MPU (Memory Protection Unit), ma tipicamente il driver opera con privilegi massimi.
- Controller: Espone registri mappati in uno spazio di indirizzamento piatto a 32-bit (es.
- PIC32 (MIPS32):
- Controller: Simile al modello ARM, i registri periferici sono mappati in memoria fisica (KSEG1 per accesso uncached).
- Driver (Harmony/PLIB): Gestisce le interruzioni tramite un Interrupt Controller vettorizzato esterno al core o integrato. Non c’è una MMU completa (nella maggior parte dei casi), ma segmentazione fissa della memoria.
B. Personal Computer (x86/x64) – Dominio General Purpose
L’hardware è astratto da un OS complesso (Linux/Windows) che utilizza la memoria virtuale.
- Controller: Risiede tipicamente su bus ad alta velocità (PCIe). Le risorse (indirizzi MMIO, IRQ) sono assegnate al boot tramite enumerazione (BIOS/UEFI) e descritte nei registri di configurazione PCI (BAR – Base Address Registers).
- Driver Kernel-Mode:
- Non accede agli indirizzi fisici grezzi. Deve mappare l’intervallo fisico della periferica (letto dai BAR) nello spazio virtuale del kernel (es. tramite
ioremapsu Linux). - Gestisce interrupt complessi come MSI/MSI-X (Message Signaled Interrupts) tramite APIC.
- Coordina il DMA utilizzando (se presente) la IOMMU per garantire che la periferica scriva solo nelle aree di memoria autorizzate (isolamento e sicurezza).
- Non accede agli indirizzi fisici grezzi. Deve mappare l’intervallo fisico della periferica (letto dai BAR) nello spazio virtuale del kernel (es. tramite
C. Simulatori Didattici (MARS / MIPS)
- Controller: Non esiste. Non vi sono registri hardware reali o logica di bus.
- Meccanismo: L’I/O è interamente emulato tramite Syscall (istruzione
syscall). Questa genera una trap (eccezione software) gestita dal simulatore (scritto in Java), che esegue l’operazione di alto livello (es. stampa su console) e restituisce il controllo. È un’astrazione funzionale, non strutturale.
2.3 Flusso di Interazione: Stack HW/SW
Il seguente diagramma illustra la gerarchia di controllo in un sistema reale (non emulato):
LIVELLO AZIONE
---------------------------------------------------------
1. [Applicazione Utente] Richiesta I/O ad alto livello
(es. printf("ciao"))
│
▼
2. [Driver SW / OS] - Traduzione logica (File Descriptor -> Periferica)
- Controllo permessi
- Scrittura sui Registri del Controller (MMIO)
│
▼
3. [Device Controller HW] - Ricezione comando dal bus
- Bufferizzazione dati (FIFO)
- Asserzione segnali elettrici (TX/RX)
- Generazione Interrupt (al termine)
│
▼
4. [Periferica Fisica] Trasmissione fisica sul mezzo
(Cavo Seriale, Onda Radio, ecc.)
2.4 Sintesi delle differenze chiave
| Caratteristica | Embedded (Cortex-M/MIPS) | PC (x86) | Simulatore (MARS) |
|---|---|---|---|
| Indirizzamento | Fisico (Direct MMIO) | Virtuale (MMIO Remapped) | Nessuno (Emulato) |
| Interrupt | NVIC / Interrupt Controller | APIC / MSI-X | Trap software |
| Accesso HW | Diretto (Puntatori) | Mediato dal Kernel (Driver) | Assente (Syscall) |
| Protezione | MPU (Opzionale) | MMU + IOMMU (Mandatoria) | N/A |
3. Interoperabilità: controller ↔ driver ↔ (IO)MMU
- STM32/PIC32 (MCU):
CPU/DMA ↔ AHB bus ↔ controller- Driver/HAL accede a MMIO fisico; IRQ su NVIC/MIPS intc; DMA è bus‑master sul fabric MCU. (Niente MMU).
- x86/PC:
Processo utente → syscall → kernel → driver- MMU separa spazi utente/kernel; IOMMU isola DMA dei device; driver effettua MMIO su BAR; IRQ via MSI/APIC.
- MARS:
programma utente → syscall → simulatore (nessun controller reale- MMU/IRQ sono concetti emulati.
STM32 (Cortex-M)
- Driver accede direttamente a registri fisici.
- IRQ gestite da NVIC, DMA da AHB bus matrix.
- No MMU, quindi nessuna traduzione VA—PA
PC (x86)
- Driver kernel usa MMU/IOMMU.
- Accessi a controuer via mapping (ioremap).
MARS/MIPS
- Syscall gestita dal simulatore: nessuna periferica reale.
4. Controllo periferiche: PC vs Microcontrollore
- PC/x86: applicazioni non parlano a registri HW; passano per syscall → kernel/driver → MMIO. Protezione/isolamento via MMU/IOMMU.
- MCU (STM32/PIC32): applicazioni (firmware) parlano direttamente ai registri tramite HAL o bare‑metal; con RTOS esistono syscall per servizi kernel, ma non per l’accesso diretto ai registri periferici (che resta ai driver/HAL).
| Architettura | MMU/MPU | Accesso periferiche | Syscall | HAL | Bare‑metal |
|---|---|---|---|---|---|
| STM32 (ARM) | MPU opz. | MMIO diretto | RTOS SVC (kernel) | HAL Cube/LL | sì |
| PIC32 (MIPS) | no MMU | MMIO diretto | opz. (con OS) | Harmony | sì |
| x86 (PC) | MMU+IOMMU | MMIO + port I/O | sì (user→kernel) | driver kernel | no |
| MARS (sim) | emulato | emulato | sì (trap simul.) | n/a | n/a |
5. syscall e implementazione ISA :MIPS32 vs ARM 👁️
Una system call (syscall) è un meccanismo per passare dal modo utente al modo kernel per richiedere un servizio protetto del sistema operativo (es. leggere da file, allocare memoria, creare un thread).
–> 📷 mostra la syscall –> LivelliAstrazioneElaboratore.png
Per fare questo serve:
- Un modo per identificare il servizio richiesto → il numero della syscall
- Mips : usa i registri architettura ISA (Linux e mars usano $v0)
- ARM: non usa i registri
- Un modo per passare parametri → registri (architettura ISA) o stack.
- Un meccanismo di trap → l’istruzione speciale (
syscallsu MIPS,SVCsu ARM).
Supervisor Call (SVC) nell’architettura ARM Cortex-M (come quella degli STM32).
È l’equivalente dell’istruzione SYSCALL su MIPS o x86, usata per invocare chiamate di sistema (system call) dal contesto utente al contesto privilegiato del kernel o RTOS.
5.1) MIPS32 in ambiente didattico (MARS/SPIM)
Nei simulatori come MARS o SPIM, le syscall sono semplificate:
- Il numero della syscall va sempre messo nel registro
$v0. - I parametri vanno in
$a0…$a3. - L’istruzione che genera la trap è sempre:
li $v0, 4 # 4 = stampa stringa
la $a0, msg # parametro stringa
syscall # trap: gestita dal simulatore`
Qui il simulatore legge $v0 per sapere quale servizio eseguire.
Quindi nei simulatori $v0 è obbligatorio.
5.2) MIPS32 reale con un sistema operativo
“MIPS32 reale con OS: numero syscall in registro/stack → trap“
Questo significa che non esiste un’unica convenzione universale come in MARS, ma dipende dal sistema operativo :
- In Linux MIPS:
- Il numero della syscall viene passato nel registro
$v0(come nel simulatore). - I parametri vanno in
$a0…$a3e, se servono più di 4, nel kernel stack. - Invocazione istruzione speciale :
syscall - l kernel legge
$v0e decide quale funzione eseguire.
- Il numero della syscall viene passato nel registro
- In altri OS MIPS:
- Alcuni passano il numero di syscall in un registro diverso da
$v0. - Oppure, per servizi complessi, il numero della syscall può essere lasciato sullo stack insieme agli altri parametri.
- Alcuni passano il numero di syscall in un registro diverso da
Quindi “numero di syscall in registro/stack” significa che a seconda dell’OS il numero che identifica la syscall può:
– essere messo in un registro (di solito$v0),
– oppure sullo stack (meno comune).
5.3) ARM/STM32: differenza chiave delle syscall con MIPS
“ARM/STM32: nessun
$v0. Parametri in r0…r3, numero servizio codificato in SVC immediato o convenzioni interne.”
- Su ARM Cortex-M non esiste
$v0. - Il numero della syscall non sta in un registro fisso, ma:
- È codificato nell’istruzione stessa (
SVC #imm). - Oppure, nei sistemi con un RTOS, il numero servizio è deciso dalle convenzioni interne del kernel e passato in r0 come primo argomento.
- È codificato nell’istruzione stessa (
Esempio ARM STM32 con SVC :
SVC #5 ; Numero della syscall = 5 codificato nell'istruzione
5.3.1) ARM Cortex-M + RTOS (es. ChibiOS)
- In MCU non c’è syscall per l’I/O come in MARS/Linux: i driver parlano direttamente ai registri (HAL/MMIO).
- Le “syscall” RTOS sono servizi kernel (sleep, IPC…), tipicamente via SVC; parametri in
r0…r3e ID servizio in immediato SVC o convenzione interna. Nessun registro dedicato tipo$v0.
5.3.2) Esempio STM32F4 + ChibiOS: uso di SVC
esempio concreto su STM32F4 + ChibiOS: uso di SVC per :
- (A) fare una mini “syscall” che somma due interi restituendo il risultato in r0 .
- (B) chiedere al kernel (in modo ISR-safe) di segnalare un evento a un thread .
È RTOS-safe perché dentro l’SVC_Handleruso solo API…I(classe I).
1. Wrapper SVC lato “utente” (thread)
Crea un header (es. svc_calls.h):
#pragma once
#include "ch.h"
#include "hal.h"
#include <stdint.h>
/* SVC #0x10: r0 = r0 + r1 (demo “arith”) */
static inline uint32_t svc_add_u32(uint32_t a, uint32_t b) {
register uint32_t r0 __asm__("r0") = a;
register uint32_t r1 __asm__("r1") = b;
__asm volatile(
"svc 0x10\n"
: "+r"(r0) /* r0 è output (ritorno) */
: "r"(r1) /* r1 è input */
: "r2","r3","lr","memory","cc");
return r0; /* valore di ritorno in r0 */
}
/* SVC #0x20: chEvtSignalI(tp, mask) (kernel request, ISR-safe) */
static inline void svc_evt_signal(thread_t *tp, eventmask_t mask) {
register thread_t *r0 __asm__("r0") = tp;
register eventmask_t r1 __asm__("r1") = mask;
__asm volatile(
"svc 0x20\n"
: "+r"(r0)
: "r"(r1)
: "r2","r3","lr","memory","cc");
}Uso in un thread ChibiOS (es. in main.c):
#include "ch.h"
#include "hal.h"
#include "svc_calls.h"
static THD_WORKING_AREA(waBlink, 256);
static THD_FUNCTION(thBlink, arg) {
(void)arg;
chRegSetThreadName("blink");
while (true) {
uint32_t s = svc_add_u32(40, 2); /* demo SVC #0x10 */
(void)s;
palToggleLine(LINE_LED_GREEN);
chThdSleepMilliseconds(500);
}
}
int main(void) {
halInit();
chSysInit();
thread_t *tpBlink = chThdCreateStatic(waBlink, sizeof(waBlink), NORMALPRIO, thBlink, NULL);
/* Demo: dopo 2 secondi segnalo un evento al thread (via SVC #0x20). */
chThdSleepMilliseconds(2000);
svc_evt_signal(tpBlink, (eventmask_t)1);
while (true) {
chThdSleepMilliseconds(1000);
}
}
2) Handler SVC (eccezione) con dispatch per imm8
Definisci SVC_Handler (override del weak) in, ad es., svc_handler.c:
#include "ch.h"
#include "hal.h"
#include <stdint.h>
#include "cmsis_os2.h"
#include "core_cm4.h" /* __get_PSP/__get_MSP */
static inline uint32_t *get_stacked_frame(void) {
/* Se il thread gira con PSP, la SVC salva su PSP; altrimenti MSP. */
register uint32_t ctrl __asm__("control");
__asm volatile ("mrs %0, control" : "=r"(ctrl));
if (ctrl & 0x2U) {
return (uint32_t *)__get_PSP(); /* thread mode with PSP */
} else {
return (uint32_t *)__get_MSP(); /* handler/main with MSP */
}
}
/*
Frame impilato automaticamente (Cortex-M, Thumb):
sp[0] = r0
sp[1] = r1
sp[2] = r2
sp[3] = r3
sp[4] = r12
sp[5] = lr (r14)
sp[6] = pc (r15) <-- istruzione successiva alla SVC
sp[7] = xPSR
*/
void SVC_Handler(void) {
uint32_t *sp = get_stacked_frame();
/* Estrai l’immediato della SVC.
In Thumb la SVC è codificata come: 0xDF <imm8>.
PC impilato punta già ALL'ISTRUZIONE SUCCESSIVA, quindi prendo pc-2. */
uint32_t svc_pc = sp[6];
uint8_t svc_imm = *(((uint8_t*)svc_pc) - 2);
switch (svc_imm) {
case 0x10: { /* r0 = r0 + r1 (demo, tutto in user-space ABI) */
uint32_t r0 = sp[0];
uint32_t r1 = sp[1];
sp[0] = r0 + r1; /* ritorno: r0 */
break;
}
case 0x20: { /* chEvtSignalI(tp, mask) → ISR-safe (classe I) */
thread_t *tp = (thread_t*)sp[0];
eventmask_t mask = (eventmask_t)sp[1];
chSysLockFromISR();
chEvtSignalI(tp, mask);
chSysUnlockFromISR();
break;
}
default:
/* opzionale: trap/ignore/log */
break;
}
}5.4) Valori di ritorno e parametri : MIPS e ARM
- MIPS32: convenzione ABI — ritorno in
$v0(e$v1per wide), parametri in$a0…$a3. - ARM (Cortex‑M): ritorno in
r0(er1per wide), parametri inr0…r3(extra su stack).
Tabella : Ruolo di $v0
| Contesto | $v0 contiene | Obbligatorio? |
|---|---|---|
| MARS / SPIM | Numero della syscall | Sì, sempre |
| MIPS Linux | Numero della syscall | Sì, per convenzione |
| Altri OS MIPS | Dipende → può essere $v0, un altro registro, o lo stack | No |
| ARM | Non esiste $v0 | No |
Tabella riassuntivo : $v , $a vs $r
| Architettura | Numero syscall | Parametri | Trap instruction | Note |
|---|---|---|---|---|
| MIPS32 (MARS) | $v0 | $a0…$a3 | syscall | Fisso e semplificato |
| MIPS32 + OS | $v0 o stack | $a0…$a3 + stack | syscall | Dipende dal kernel |
| ARM Cortex-M | SVC #imm o r0 | r0…r3 | SVC | Nessun $v0 |
⚠️Nota lSA MIPS real:
– I registri come Sv0,$v1 : convenzionalmente usati per valori di ritorno .
– I registri $a0...$a3 : convenzionalmente usati per il passaggio dei parametri .
👁️Esempio gestione ritorno e parametri ARM: Funzione in C e assembly (STM32):
int add(int a, int b) { return a+b; }add:
adds r0, r0, r1 # somma parametri r0,r1 → ritorno in r0
bx lrPerché MIPS usa $v (valore ritorno) e $a(parametri) e ARM solo r0…r3 (sia per valori ritorno e parametri)?
- MIPS 32 separa registri di ritorno (
$v0,$v1) da registri argomenti ($a0…$a3). Gli argomenti sono 4; i registri di ritorno sono 1–2 a parte. Non è “5 contemporanei”: sono ruoli diversi. - ARM cortex-m riusa gli stessi registri:
r0…r3per i primi 4 argomenti er0(or0:r1) per il valore di ritorno. È una scelta di convenzione ABI, non di “potenza” dell’ISA.
⚠️ In entrambi i mondi, se gli argomenti superano 4 si passa dallo stack;
se il ritorno è >32 bit, si usano due registri (MIPS: $v1:$v0, ARM: r1:r0).
6. Gestione periferiche : Syscall vs HAL vs Bare Metal 👁️
| Architettura | MMU/MPU | Accesso periferiche | Syscall | HAL | Bare‑metal |
|---|---|---|---|---|---|
| STM32 (ARM) | MPU opz. | MMIO diretto | RTOS SVC (kernel) | HAL Cube/LL | sì |
| PIC32 (MIPS) | no MMU | MMIO diretto | opz. (con OS) | Harmony | sì |
| x86 (PC) | MMU+IOMMU | MMIO + port I/O | sì (user→kernel) | driver kernel | no |
| MARS (sim) | emulato | emulato | sì (trap simul.) | n/a | n/a |
6.1) Syscall
Syscall su MIPS Emulato (MARS)
- Istruzione
syscallprovoca un trap → eseguito da OS/simulatore. - Codice servizio in
$v0definisce quale operazione fare (es.4 = print_string). - I registri
$a0…$a3passano parametri. - Nessun controller HW: la periferica è simulata.
Syscall su MIPS reale (es. PIC32 con OS)
syscallgenera un eccezione software. L’handler OS nel kernel decide l’azione (es. leggere UART). Non c’è simulatore: la syscall entra nel kernel, che poi parla ai driver reali.
Syscall su ChibiOS (STM32)
- Sono chiamate a servizi kernel (es. API kernel
chThdSleepMilliseconds). - Non manipolano direttamente periferiche: per quello ci sono driver/HAL.
- Differenza con MARS: qui la syscall è reale (chiamata in modalità privilegiata al kernel), non simulata.
6.2) HAL (STM32)
- Libreria firmware che manipola registri periferiche reali.
- Espone API C (
HAL_UART_Transmit,HAL_GPIO_WritePin). - Interagisce con NVIC/DMA.
Operazioni e valori di ritorno: HAL STM32 vs HAL PIC32
STM32 HAL: status HAL_OK/ERROR/BUSY/TIMEOUT.
PIC32 Harmony: status/handle (DRV_HANDLE, SYS_STATUS).
- STM32 HAL: status via
HAL_StatusTypeDef(HAL_OK,HAL_ERROR,HAL_BUSY,HAL_TIMEOUT); spesso API blocking con timeout, o non‑blocking con IRQ/DMA + callback. - PIC32 (Harmony/driver): handle e status (es.
DRV_HANDLE,DRV_CLIENT_STATUS,SYS_STATUS), callback/event model; MMIO sottostante simile a STM32.
Pattern comune: API restituisce status/handle; i dati viaggiano via buffer; operazioni async con IRQ/DMA notificano via callback/event
6.3) Bare metal : STM32 vs PIC32
- STM32: registri Cortex‑M, NVIC, driver scritti a mano. Massimo controllo, nessuna protezione.
- PIC32: modello identico: registri MIPS32, INT controller, accesso diretto.
6.4) Syscall VS HAL VS bare‑metal
- Syscall = concetto proprio di un OS/RTOS. Serve per passare dal codice utente a servizi kernel in modo controllato.
- RTOS (ChibiOS, FreeRTOS): hanno syscall interne (API di kernel), ma per periferiche .
- HAL : Le API C (rilasciati dalle librerei) accedono direttamente ai registri periferiche (nessuna astrazione kernel).
- Bare‑metal senza OS = accesso diretto ai registri (nessuna astrazione) non esistono syscall ne API HAL .
⚠️ La differenza è che in RTOS (ChibiOS) le syscall esistono per servizi kernel, mentre per periferiche si usano driver/HAL.
6.5) Esempi
syscall
MARS
li $v0, 4
la $a0, msg
syscallPIC32 con OS
write(fd_uart, buf, len);
STM32 ChbiOS
chThdSleepMilliseconds(10);
HAL
PIC32
DRV_USART_WriteBuffer(h, "ciao", 4);
STM32
HAL_UART_Transmit(&huart2, (uint8_t*)"ciao", 4, HAL_MAX_DELAY);
Bare-Metal
PIC
LATBINV = (1u << 7);
STM32
GPIOA->ODR ^= (1u << 5);
7. Tabella di confronto approfondita (estesa)
| Architettura | Gestione memoria | Interconnessione bus | Percorso accesso I/O | Syscall (meccanismo) | ISA ABI (ritorno/parametri) | IRQ controller | DMA / Bus mastering | Driver/HAL tipici | Pro | Contro |
| STM32 (ARM Cortex‑M) | MPU opz., no MMU, indirizzi fisici; no IOMMU | AHB multi‑layer bus matrix (arbitraggio per‑slave), APB single‑master | App/Driver → HAL → MMIO (registri), opz. bare‑metal | RTOS SVC per servizi kernel (no I/O diretto); parametri in r0…r3 | r0 ritorno; r0…r3 parametri | NVIC vettorizzato, priorità/nesting, EXTI | DMA1/DMA2 master su AHB; periferiche con DMA | ST HAL/Cube/LL, driver ChibiOS | Latenza bassa, controllo diretto, semplice debug | Meno protezione, rischio race/ordering, gestione manuale di IRQ/DMA |
| PIC32 (MIPS32 MCU) | Tipicamente no MMU (indirizzi fisici); talvolta protezioni basiche tipo regioni; no IOMMU | Bus MCU (variant‑dependent), accesso MMIO; spesso DMA on‑chip | App/Driver → HAL → MMIO; opz. bare‑metal | Con OS: eccezione software syscall → kernel → driver; senza OS: non usata per I/O | $v0 ritorno; a0…a3 parametri (ABI O32) | EVIC/INTC (dipende dalla serie), vettorizzato | DMA on‑chip come master | Harmony (Microchip), driver BSP | Modello simile a STM32, buon throughput DMA | Manca protezione MMU; toolchain/stack variegati |
| x86 (PC) | MMU (paging, TLB), IOMMU per DMA; spazio virtuale user/kernel | PCIe fabric, controller memoria integrato CPU; APIC/MSI | User → syscall → kernel → driver → MMIO | Istruzioni SYSCALL/SYSENTER (o int 0x80 legacy); ABI syscalls (es. rax=nr) | ABI SysV: rax ritorno; funzioni: r0.. ma per syscalls: rdi,rsi,rdx,r10,r8,r9 | APIC/MSI/MSI‑X | Dispositivi PCIe bus‑master via IOMMU | Driver kernel (Linux/Windows), frameworks (NDIS, ALSA, etc.) | Protezione/isolamento, multi‑processo, hot‑plug | Latenza maggiore, complessità driver, overhead di contesto |
| MARS (MIPS emulato) | Emulata; nessuna periferica reale | N/A (simulatore) | Programma → **syscall** → simulatore | syscall con $v0=codice servizio; il simulatore esegue | $v0 ritorno/codice; a0…a3 parametri (convenzione MARS) | Emulato | N/A | N/A | Didattico, chiarisce ABI e trap | Non rappresenta HW reale/I/O MMIO |
Nota: su ARM/STM32 l’operazione di “chiamata di sistema” è codificata nell’istruzione SVC (immediato) e nelle convenzioni del kernel; non esiste un registro dedicato stile $v0 per definire il servizio.
8. Conclusioni
Il confronto tra MIPS32, ARM Cortex-M e x86 mette in evidenza come la gestione delle periferiche e della memoria dipenda strettamente dal contesto architetturale e dal modello d’uso previsto.
Nei sistemi PC (x86), l’MMU e i driver in kernel space impongono un passaggio obbligato attraverso le syscall per ogni operazione di I/O, garantendo protezione e isolamento.
Nei microcontrollori (STM32, PIC32) invece l’approccio è molto più diretto: l’assenza di una MMU e la presenza al massimo di un’MPU rende l’accesso ai registri delle periferiche immediato, tramite HAL o bare-metal. Le syscall in questo scenario esistono solo come astrazione dei servizi di un RTOS (es. ChibiOS), non come strumento universale di accesso all’hardware.
Infine, il caso di MARS (MIPS emulato) mostra l’uso didattico della syscall come convenzione simulata, lontana dal comportamento delle macchine reali. In sintesi, la distinzione tra controller HW e driver SW, unita al ruolo della MMU/MPU, evidenzia come cambino le strategie di progettazione software: dal mondo PC basato su protezione e astrazione, al mondo embedded dove prevalgono immediatezza, efficienza e controllo totale dell’hardware.
9. Glossario
- MMU: Memory Management Unit (traduzione indirizzi virtuali → fisici).
- MPU: Memory Protection Unit (definisce protezioni su regioni fisiche).
- MMIO: Memory Mapped I/O (registri periferiche in spazio memoria).
- ISR: Interrupt Service Routine.
- HAL: Hardware Abstraction Layer.
- BSP: Board Support Package.
- Syscall: invocazione di servizio kernel via eccezione/trap.
- IOMMU: MMU per accessi DMA delle periferiche.
- ABI: Application Binary Interface (convenzioni registri/stack).
- SCV: supervisor call (equivalente a syscall in ARM)
- trap : Per “trap al kernel” intendo una eccezione sincrona generata dalla CPU che cambia il flusso dal codice utente a codice privilegiato (kernel/supervisor) per eseguire un servizio o gestire un evento (es. syscall, fault, breakpoint). È diversa da un interrupt (asincrono, generato da periferiche).