
Se per un programmatore di media esperienza la gestione di un processore “single-core” non rappresenta un problema, le cose possono complicarsi anche notevolmente nel caso in cui i “core” da gestire siano più di uno. In questo articolo della Rubrica Firmware Reload, contenente articoli della rivista cartacea Firmware, affrontiamo in maniera esaustiva questa complessa materia.
COMUNICAZIONE TRA I CORE
La famiglia KeyStone dei dispositivi TCI66xx e C66xx della Texas Instruments, allo stesso modo di altri più datati (serie 64xx), offre alcuni meccanismi strutturali per supportare le comunicazioni tra processori; tutti i core hanno pieno accesso alla mappatura della memoria del dispositivo, il che vuol dire che ciascuno di essi può leggere e scrivere in qualunque locazione di memoria. Inoltre, esiste il supporto diretto per la segnalazione degli eventi tra ciascun core così come il controllo DMA per applicazione di terze parti. Il modulo Multicore Navigation, disponibile sulla maggior parte dei dispositivi di questa famiglia, fornisce un meccanismo efficiente per la sincronizzazione tra i core, la comunicazione ed il trasferimento dei dati tra gli stessi, ed anche un accesso facilitato a periferiche ad alto bitrate. La comunicazione, insomma, di fatto riguarda due azioni fondamentali: lo spostamento dei dati e la notifica. E di queste due, l’ultima include i problemi di sincronizzazione e la loro gestione. Riguardo lo spostamento dei dati, esso si riferisce al trasferimento fisico che può essere effettuato secondo diverse tecniche, prima di tutto l’utilizzo di un “message buffer” condiviso, in cui sia chi spedisce sia chi riceve il messaggio ha accesso alla stessa locazione di memoria (fisica). È possibile anche che venga utilizzata della memoria dedicata oppure che si utilizzi un buffer di memoria “transitioned”, in cui la proprietà della memoria viene concessa dal sender al receiver del messaggio ma il contenuto non viene trasferito.
Per ciascuna di queste soluzioni ci sono due modi per effettuare lettura e scrittura della memoria: load/store della CPU o DMA transfer, e ciascuno di essi può essere configurato per utilizzare metodi differenti. Se la memoria è condivisa, per esempio, non vuol dire necessariamente che venga utilizzata una tabella di partizione che divide tutta la memoria disponibile in parti uguali ma piuttosto che il message buffer si trova in una specifica locazione di memoria accessibile ai diretti interessati e che ciascuno di essi è responsabile per la sua “parte della transazione”. Se le operazioni si svolgono su più core distinti, è assolutamente indispensabile mantenere la coerenza. La memoria può essere dedicata e questo scenario funzionale permette di gestire la transazione tra locazioni diverse. Si ricorre a questa tecnica, di solito, quando sono in uso aree di memoria dedicata all’interno di una memoria condivisa per ciascun core oppure di una memoria locale. Lo spostamento dei dati può essere fatto tramite comunicazione diretta tra i core ed è tipicamente quello che viene fatto dal modulo Multicore Navigation della famiglia KeyStone. Una soluzione ancora più intelligente è rappresentata dalla memoria “transitioned” di cui abbiamo accennato in precedenza: dal momento che è possibile che sia il sender sia il receiver utilizzino la stessa locazione di memoria ma che non siano in grado di comunicare durante il trasferimento, la memoria potrebbe, di fatto, essere sempre condivisa e quindi non essere temporanea. Piuttosto che trasferire il dato, viene trasferita la “proprietà” del dato per cui la locazione di memoria su cui viene scritto il dato rimane sempre la stessa, e di fatto i due soggetti, sender e receiver, si scambiano un puntatore alla locazione di memoria che ne garantisce e permette l’accesso. Il modulo di cui abbiamo parlato fino a questo momento permette di incapsulare i messaggi e li sposta in code utilizzando un sistema di gestione che prende il nome di Queue Manager Subsystem, il vero e proprio cuore pulsante del Multicore Navigation.
Esso controlla, infatti, il comportamento delle code hardware e permette il routing dei descriptor. Fondamentali sono, dunque, le notifiche e la sincronizzazione, che nel modello multicore richiede l’abilità di sincronizzare i core tra loro, come abbiamo avuto modo di dire in precedenza. Un caso tipico si verifica quando un singolo core si occupa dell’inizializzazione del sistema mentre tutti gli altri devono attendere che questo processo sia completo prima di poter eseguire qualunque genere di compito che gli sia stato assegnato. Anche il trasporto di dati da un core all’altro richiede notifiche e sincronizzazione ed anche in questo caso il Multicore Navigator si propone come un’ottima soluzione basata su due metodi distinti che vediamo rappresentati anche grazie alle Figure 1 e 2. Nel signaling diretto, quello rappresentato dalla Figura 1, il dispositivo supporta una semplice periferica che permette al core di generare un evento fisico per un altro core (uno qualunque degli altri disponibili).

Figura 1: Trasporto dati con signaling diretto
Questo evento viene condotto attraverso i vari sistemi ed il programmatore può selezionare se questo genererà un interrupt per la CPU oppure no. Viene incluso anche un registro di flag per indicare chi ha generato l’evento, nel caso in cui sia necessario saperlo per poter decidere che cosa fare. Il metodo, in qualche modo, duale è quello indiretto. Nel caso di Figura 2, se, per esempio, un controller EDMA viene utilizzato per spostare dei dati, la segnalazione tra core può avvenire proprio grazie a questo trasporto e pertanto la notifica segue lo spostamento dei dati in hardware piuttosto che controllarla.

Figura 2: Trasporto dati con signaling indiretto
I METODI
Come abbiamo capito, il Multicore Navigator è un sistema che gestisce in maniera efficace la comunicazione soprattutto dal punto di vista dei metodi di notifica. Proprio a riguardo, è necessario specificare che esso si occupa di incapsulare i messaggi, compreso quelli che contengono dati e che vengono chiamati “descrittori”, e li sposta tra code hardware. Ciascuna destinazione ha una o più code di ricezione. Sono possibili i seguenti metodi:
- non-blocking polling
- blocking polling
- interrupt-based notification
- delayed interrupt notification
- QoS-based notification
TRASFERIMENTO DEI DATI
Sempre considerando dispositivi di cui abbiamo parlato fino a questo momento, si occupano del trasferimento dei dati l’EDMA (enhanced) ed il PKTDMA (Packet DMA). Per comunicazioni highbit tra dispositivi ci sono diversi motori di trasferimento che possono essere scelti sulla base delle interfacce fisiche selezionate per la comunicazione. Le possibilità includono:
- antenna interface (wireless), in cui vengono utilizzate più istanze PKTDMA insieme con altre AIF
- RapidIO seriale
- Ethernet
- PCI express
- Hyperlink
Leggi anche le puntate precedenti:
La programmazione Multicore – Parte 1 | Elettronica Open Source
La programmazione Multicore – Parte 2 | Elettronica Open Source
