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:

  1. Configurazione e Controllo: Invia comandi al controller e ne verifica lo stato (es. polling del bit Ready o Done).
  2. 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):

  1. 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.
  2. 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.
  3. 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

  1. 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 (MMIOMemory 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.
  2. 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.

  1. STM32 (ARM Cortex-M):
    • Controller: Espone registri mappati in uno spazio di indirizzamento piatto a 32-bit (es. USART1 base address 0x40013800). Include registri come CR1 (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.
  2. 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 (BARBase 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 ioremap su 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).

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

CaratteristicaEmbedded (Cortex-M/MIPS)PC (x86)Simulatore (MARS)
IndirizzamentoFisico (Direct MMIO)Virtuale (MMIO Remapped)Nessuno (Emulato)
InterruptNVIC / Interrupt ControllerAPIC / MSI-XTrap software
Accesso HWDiretto (Puntatori)Mediato dal Kernel (Driver)Assente (Syscall)
ProtezioneMPU (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 syscallkernel/driverMMIO. 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).
ArchitetturaMMU/MPUAccesso perifericheSyscallHALBare‑metal
STM32 (ARM)MPU opz.MMIO direttoRTOS SVC (kernel)HAL Cube/LL
PIC32 (MIPS)no MMUMMIO direttoopz. (con OS)Harmony
x86 (PC)MMU+IOMMUMMIO + port I/Osì (user→kernel)driver kernelno
MARS (sim)emulatoemulatosì (trap simul.)n/an/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 (syscall su MIPS, SVC su 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:
    1. Il numero della syscall viene passato nel registro $v0 (come nel simulatore).
    2. I parametri vanno in $a0…$a3 e, se servono più di 4, nel kernel stack.
    3. Invocazione istruzione speciale : syscall
    4. l kernel legge $v0 e decide quale funzione eseguire.
  • 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.

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.

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…r3 e 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_Handler uso 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 $v1 per wide), parametri in $a0…$a3.
  • ARM (Cortex‑M): ritorno in r0 (e r1 per wide), parametri in r0…r3 (extra su stack).

Tabella : Ruolo di $v0

Contesto$v0 contieneObbligatorio?
MARS / SPIMNumero della syscall, sempre
MIPS LinuxNumero della syscall, per convenzione
Altri OS MIPSDipende → può essere $v0, un altro registro, o lo stackNo
ARMNon esiste $v0No

Tabella riassuntivo : $v , $a vs $r

ArchitetturaNumero syscallParametriTrap instructionNote
MIPS32 (MARS)$v0$a0…$a3syscallFisso e semplificato
MIPS32 + OS$v0 o stack$a0…$a3 + stacksyscallDipende dal kernel
ARM Cortex-MSVC #imm o r0r0…r3SVCNessun $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   lr

Perché 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…r3 per i primi 4 argomenti e r0 (o r0: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 👁️

ArchitetturaMMU/MPUAccesso perifericheSyscallHALBare‑metal
STM32 (ARM)MPU opz.MMIO direttoRTOS SVC (kernel)HAL Cube/LL
PIC32 (MIPS)no MMUMMIO direttoopz. (con OS)Harmony
x86 (PC)MMU+IOMMUMMIO + port I/Osì (user→kernel)driver kernelno
MARS (sim)emulatoemulatosì (trap simul.)n/an/a

6.1) Syscall

Syscall su MIPS Emulato (MARS)

  • Istruzione syscall provoca un trap → eseguito da OS/simulatore.
  • Codice servizio in $v0 definisce quale operazione fare (es. 4 = print_string).
  • I registri $a0…$a3 passano parametri.
  • Nessun controller HW: la periferica è simulata.

Syscall su MIPS reale (es. PIC32 con OS)

  • syscall genera 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

li $v0, 4
la $a0, msg
syscall
write(fd_uart, buf, len);
chThdSleepMilliseconds(10);

HAL

DRV_USART_WriteBuffer(h, "ciao", 4);
HAL_UART_Transmit(&huart2, (uint8_t*)"ciao", 4, HAL_MAX_DELAY);

Bare-Metal

LATBINV = (1u << 7);
GPIOA->ODR ^= (1u << 5);

7. Tabella di confronto approfondita (estesa)

ArchitetturaGestione memoriaInterconnessione busPercorso accesso I/OSyscall (meccanismo)ISA ABI (ritorno/parametri)IRQ controllerDMA / Bus masteringDriver/HAL tipiciProContro
STM32 (ARM Cortex‑M)MPU opz., no MMU, indirizzi fisici; no IOMMUAHB multi‑layer bus matrix (arbitraggio per‑slave), APB single‑masterApp/Driver → HAL → MMIO (registri), opz. bare‑metalRTOS SVC per servizi kernel (no I/O diretto); parametri in r0…r3r0 ritorno; r0…r3 parametriNVIC vettorizzato, priorità/nesting, EXTIDMA1/DMA2 master su AHB; periferiche con DMAST HAL/Cube/LL, driver ChibiOSLatenza bassa, controllo diretto, semplice debugMeno protezione, rischio race/ordering, gestione manuale di IRQ/DMA
PIC32 (MIPS32 MCU)Tipicamente no MMU (indirizzi fisici); talvolta protezioni basiche tipo regioni; no IOMMUBus MCU (variant‑dependent), accesso MMIO; spesso DMA on‑chipApp/Driver → HAL → MMIO; opz. bare‑metalCon 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), vettorizzatoDMA on‑chip come masterHarmony (Microchip), driver BSPModello simile a STM32, buon throughput DMAManca protezione MMU; toolchain/stack variegati
x86 (PC)MMU (paging, TLB), IOMMU per DMA; spazio virtuale user/kernelPCIe fabric, controller memoria integrato CPU; APIC/MSIUser → syscall → kernel → driver → MMIOIstruzioni 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,r9APIC/MSI/MSI‑XDispositivi PCIe bus‑master via IOMMUDriver kernel (Linux/Windows), frameworks (NDIS, ALSA, etc.)Protezione/isolamento, multi‑processo, hot‑plugLatenza maggiore, complessità driver, overhead di contesto
MARS (MIPS emulato)Emulata; nessuna periferica realeN/A (simulatore)Programma **syscall** → simulatoresyscall con $v0=codice servizio; il simulatore esegue$v0 ritorno/codice; a0…a3 parametri (convenzione MARS)EmulatoN/AN/ADidattico, chiarisce ABI e trapNon 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).