Il software e il bus PCI

L’acronimo PCI sta per Peripheral Component Interconnect e si occupa, così come dice il suo nome, di interconnettere diverse periferiche su un unico bus garantendo l’interoperabilità delle periferiche con un alto data rate al fine di rispondere ai moderni controllori grafici, dispositivi di massa o interfacce di rete.

Il PCI è stato realizzato allo scopo di ottenere un alto data rate nei trasferimenti di I/O con la particolare attenzione di non legarsi alle caratteristiche del processore utilizzato, nel senso che il bus PCI ha un protocollo che risulta indipendente dal processore. Da un punto di vista implementativo questa particolarità è stata realizzata utilizzando un bridge PCI-Host. La figura 3 mostra il diagramma a blocchi del bus PCI. Il bridge dipende dal processore al quale è interfacciato e deve essere progettato in funzione della CPU cui è connesso: l’insieme processore bridge deve essere però indipendente dall’architettura utilizzata. In un sottosistema PCI esistono quattro componenti: Bus Number, Device Number, Function Number e Register Number. Secondo lo standard esistono fino a 256 bus su un sistema di questo tipo, anche se nella maggior parte delle applicazioni ne sono utilizzati solamente due: bus 0 e bus 1. È buona norma, però, effettuare la scansione di tutti i 256 bus, anche se questo può incidere sulle prestazioni, perché non si può dare per scontato che in un design sono utilizzati solo due bus. Un device è semplicemente un componente fisico inserito su di un bus, questo potrebbe essere una card video, una schedina Ethernet. Su ogni bus è possibile posizionare fino a 32 dispositivi, o devices, che il sistema può riconoscere e configurare. In realtà questo è solo un valore teorico, in effetti, il limite fisico è di gran lunga inferiore  a causa di problemi di carico. Ad ogni modo, il limite elettrico dovuto al carico sulle linee del bus impone un massimo di 10-12 dispositivi. Di sicuro, più alto è il numero di carichi presenti sul bus e maggiore è il tempo di propagazione del segnale. Non solo, bisogna anche considerare che le periferiche alloggiate sul bus, mediante connettori, contano come due carichi, poiché il connettore e la scheda rientrano tra i limiti imposti. Per questo motivo, il numero massimo di slot presenti in una motherboard, o in un nostro sistema, non può essere superiore ad almeno cinque dispositivi. L’unico modo per inserire un numero superiore di dispositivi è quello di aggiungere nell’architettura hardware un bridge PCI-PCI, creando così un altro bus capace di ospitare a sua volta 10-12 dispositivi. Generalmente, su una motherboard, o in un sistema hardware, il software deve, in ogni modo, effettuare la scansione del bus prevedendo tutti i 32 dispositivi. I dispositivi presenti in un sistema hardware devono svolgere almeno una funzione. Esistono, però, anche i dispositivi multi-funzione, come ad esempio un modem/scheda audio. I dispositivi di questo tipo sono di solito numerate sul bus come 0 e 1. In questo modo esistono otto possibili funzioni per dispositivo, da 0 a 7. Ogni funzione di un dispositivo di I/O connesso sul bus PCI dispone di una zona di 256 registri a otto bit, zona di memoria di 256 byte nella quale sono contenuti i suoi registri di configurazione. Questa zona di memoria è denominata spazio di configurazione del dispositivo, ed è suddivisa in due regioni, una detta di header (64 byte) simile per tutti i dispositivi, l’altra invece (192 byte) dipende dallo specifico host. L’header di configurazione ha un ruolo chiave nella gestione dei device e contiene una serie di registri di lettura e scrittura. I suoi primi 16 byte (PCI Device Independent Region) sono uguali per tutti gli host e memorizzano delle informazioni fondamentali, come ad esempio il loro numero identificativo (Device ID – 16 bit), il produttore (Vendor ID – 16 bit) oppure la revisione (Revision ID – 8 bit). Gli altri permettono invece di aggiungere delle ulteriori funzionalità ai dispositivi PCI (es. memorie FIFO), si veda la tabella 1 per maggiori informazioni e la figura 2 per vedere la sua rappresentazione grafica.

Tabella 1 – Layout PCI configuration header

Tabella 1 – Layout PCI configuration header

BUS PCI

Un bus di tipo PCI può essere, in alternativa, a 32 o 64 bit con una frequenza di funzionamento di 25 o 33 MHz. Stando alle specifiche fornite, le velocità di trasferimento dati, nel caso ad esempio di una frequenza di 33 MHz e utilizzando un bus a 32 e 64 bit, sono rispettivamente di 132 e 264 Mbyte/s. Un bus di questo tipo è di tipo multimaster, nel senso che può accettare più dispositivi di controllo sullo stesso bus. I vari master presenti effettuano la richiesta del bus all’arbiter il quale decide, in base alla priorità stabilita, se fornire o meno l’accesso. Molti dei segnali che viaggiano sul bus PCI sono in posti in multiplex tra loro: per questo motivo i cicli di accesso alle linee del bus sono suddivisi in due fasi, la Address phase e la Data phase. Durante la prima fase vengono inviati sul bus segnali di indirizzo e di comando, mentre nella seconda si inviano segnali di dato e di abilitazione delle linee. Tutti i segnali possono essere raggruppati in alcune categorie logiche dipendenti dalla funzione svolta. Nel caso del bus PCI a 32 bit, i principali gruppi sono costituiti da segnali di sistema, segnali di indirizzo e dato, segnali di controllo dell’interfaccia, segnali di arbitraggio, segnali di errore e quelli di interrupt. Le differenze tra questi segnali sono di tipo, com’è ovvio, di tipo funzionale. Il gruppo identificato come segnali di sistema comprendono i diversi segnali di I/O, quale la frequenza di funzionamento e il segnale di reset. I segnali di indirizzo e dato raggruppano in pratica 32 linee che sovrintendono, in multiplex, ai segnali di indirizzo e di dato. Non solo, altri quattro linee (C/BE) sono utilizzate per la codifica dei comandi che vengono richiesti da un master ad uno slave, e per l’abilitazione dei byte di dato. I segnali di controllo dell’interfaccia sono utilizzati per controllare i dispositivi presenti nel nostro sistema, quali la gestione/segnalazione dello stato di accesso al bus. I segnali di arbitraggio e di errore sono due gruppi di estrema importanza. Il segnale di errore è costituito da due bit utilizzati per controllare la parità delle transizioni sul bus dati e degli indirizzi. Non solo, questo modulo è anche utilizzato per l’invio di particolari segnali, errori, di particolare entità. Al contrario, i segnali di arbitraggio sono costituiti da due linee per ogni dispositivo utilizzati dal master per richiedere l’arbitraggio del bus o perla conferma dell’accesso.

BUS PCI EXPRESS

Questo particolare bus rappresenta l’evoluzione del PCI ed è indicato come PCIe. Il bus PCIe si basa sui concetti elementari alla base del bus precedente, ma che ne introduce un diverso livello di comunicazione non più basato sul concetto di bus, ma su un’architettura a stella dove le singole componenti comunicano in maniera seriale. L’attuale architettura garantisce una maggiore flessibilità. Infatti, oltre a offrire una maggiore velocità di comunicazione richiede minimi cambiamenti alle periferiche che possono essere quindi facilmente adattate per poter operare sul PCI Express. I dispositivi che hanno trovato particolare sollievo dall’introduzione del bus PCIe sono senza dubbio le schede video che hanno visto sostituire i vecchi bus AGP. Non solo, l’adesione di un bus seriale in luogo di uno parallelo ha semplificato di molto il design di una board. Grazie a questa soluzione si è potuto introdurre estrema modularità al progetto aggregando canali per aumentare la banda passante disponibile o per supportare particolari configurazioni come l’utilizzo di due o più schede video. Si rammenta che la bandwidth di ciascun canale è indipendente da quella degli altri. Un singolo canale PCIe, anche chiamato x1, offre una bandwidth full duplex di 266 MBytes/sec. I connettori PCIe per schede video sono generalmente costituiti da 16 canali offrendo una velocità doppia rispetto allo standard AGP 8x. Qualcuno forse si ricorderà dei primi chipset in commercio che offrivano un massimo di 20 canali, pertanto nel caso di un utilizzo simultaneo di due schede grafiche, come è possibile attraverso l’impiego della tecnologia sviluppata da Nvidia nota con l’acronimo SLI (Scalable Link Interface), i canali si riducono a 8x per ciascuna scheda, senza tuttavia evidenziare decadimenti prestazionali. Un altro aspetto da non sottovalutare è la possibilità di supportare le richieste di potenza che le ultime soluzioni grafiche hanno sempre più necessità. Infatti, a differenza dello slot AGP in grado di erogare un massimo di 50 W, l’attuale revisione di PCIe supporta carichi fino a 75W, permettendo così di eliminare il connettore MOLEX di alimentazione da molte schede.

USARE IL BUS PCI

Una tipica rappresentazione del bus PCI è posta in evidenza nella figura 2, mentre la figura 1 mostra il valore, in esadecimale, della zona in questione.

Figura 1: dump PCI.

Figura 1: dump PCI.

 

Figura 2: memory map PCI.

Figura 2: memory map PCI.

 

Figura 3: architettura PCI

Figura 3: architettura PCI

Vediamo che il VendorID è un valore a 16 bit con il valore 10B7h, mentre i successivi due byte è il DeviceID con 9055h. Ogni costruttore ha il suo VendorID, nel nostro caso il valore 10B7h è attribuito a 3COM. Per accedere ai registri nello spazio PCI Space è necessario per prima cosa inviare un index (puntatore al registro che si vuole accedere) alla porta associata (index port) e successivamente leggere, o scrivere, dati alla porta. Nello spazio di indirizzamento per x86, l’index è sempre posizionato all’indirizzo 0xcf8 e la data port a 0xcfc. Esistono due modi per cercare un dispositivo su un bus PCI. Il primo modo è quello più semplice, in sostanza si ricorre ad un software che si interpone tra il bus PCI e la nostra applicazione. Tipicamente ci troviamo in questa situazione quanto stiamo utilizzando un sistema operativo, magari Linux in versione embedded, o, in ambito PC, il BIOS. In questo caso basta invocare direttamente le procedure di interfaccia come mostra il listato 1.

INTEL_VENDOR_ID   EQU   8086h  ;  intel’s unique sig #
INTEL_EXP_NIC     EQU   1227h  ;  sample PCI device etherexpress 10/100 NIC
.386
mov               ax, 0b102h ; interrupt 1a function b102
mov               dx, INTEL_VENDOR_ID
mov               cx, INTEL_EXP_NIC
xor               si, si ; 0=1st device, 1=2nd etc.
int               1ah
jc                nope
nope:
Listato 1 – Uso del BIOS

Al contrario, in situazioni dove non è possibile utilizzare il BIOS dobbiamo sfruttare i cicli di configurazione del bus PCI. La rappresentazione dei cicli di configurazione è mostrata in figura 4.

Figura 4: cicli di configurazione.

Figura 4: cicli di configurazione.

Per prima cosa occorre spedire un valore codificato in base alla rappresentazione in figura verso l’index port (0xCF8) e successivamente leggere un valore su 32 bit dalla data port (0xCFC). Il listato 2 mostra la lettura del vendor e device ID al bus 0, device 7 e funtion 3.

BUS        EQU    0
DEV        EQU    7
FN         EQU    3
VEN_ID     EQU    0            ; vendor ID=PCI regs 0,1
PCI_INDEX  EQU    0CF8h
PCI_DATA   EQU    0CFCh
.386
           mov ax, 8000h       ; set bit 31 (after shift)
           or al, BUS          ; add in bus number
           shl eax, 16
           mov ax, DEV
           shl ax, 11          ; slide device # up to bits 15:11
           mov al, FN
           or ah, al           ; add function into bits 10:8
           mov al, VEN_ID
           cli
           mov dx, PCI_INDEX
           out dx, eax          ; send our request out
           mov dx, PCI_DATA
           in eax, dx           ; read back 32bit value.
           sti
Listato 2 – Assenza del BIOS

Tutti i registri sul PCI hanno una profondità di 8 bit. Online sono scaricabili altri listati che pongono in evidenza una possibile implementazione di una procedura di scansione del bus.

Una risposta

  1. Stefano Lovati Stefano Lovati 13 marzo 2019

Scrivi un commento

EOS-Academy