Che materia stai cercando?

Programmable Interrupt Controller Appunti scolastici Premium

Tesina di Calcolatori elettronici II del prof. Mazzeo su Implementazione VHDL di un Programmable Interrupt Controller (PIC) mod. Intel 8259A: PIC 8259A/simple, Interfaccia del dispositivo 8259A e indirizzamento, Progettazione VHDL di basso livello, Implementazione dei componenti.

Esame di Calcolatori Elettronici II docente Prof. A. Mazzeo

Anteprima

ESTRATTO DOCUMENTO

Analizziamo subito la particolare modalità di indirizzamento del PIC: un controller PIC ha due indirizzi, uno pari ed

uno dispari. I bit più significativi dell’indirizzo vengono confrontati con i bit più significativi del bus indirizzi per

determinare se il PIC è stato selezionato, mentre il bit meno significativo (A0) viene inviato direttamente al PIC. Ciò ci

permette di indirizzare due registri mediante il bus indirizzi. In realtà, il PIC contiene al suo interno un particolare

meccanismo di indirizzamento che usa il bit A0 in concomitanza con alcuni bit della parola dati (D7-D0) per

indirizzare più di due registri. Questo perché il PIC ha 4 registri ICW (Instruction Command Word) e 3 OCW

(Operation Command Word) da 8 bit ognuno.

Ad esempio, ICW2 viene indirizzato in scrittura dopo aver scritto su ICW1. La sincronizzazione delle operazioni

avviene attraverso pulsazioni del segnale INTA in logica 0. Programmare il PIC significa, come già illustrato, definirne

la modalità di funzionamento: è possibile impostare la modalità di selezione delle priorità, il tipo di protocollo

(perché si può usare uno stesso 8259A su diverse architetture)o la maschera.

Nella nostra implementazione, apporteremo delle semplificazioni al protocollo di interrupt, e non andremo a

prevedere alcuna programmazione.

In particolare, dal punto di vista comportamentale, ci aspettiamo quanto segue:

1. Quando il PIC riceve un interrupt su una o più linee IRx, determina il dispositivo con maggiore priorità

attraverso la regola semplice dell’ID minore, quindi alza il segnale INT

2. Il processore riceve INT e invia un impulso negativo di INTA al PIC

3. Sul primo impulso INTA, il PIC abbassa INT e scrive sul bus dati l’identificativo del dispositivo nei bit

meno significativi (D2-D0)

4. Il processore legge l’identificativo e si prepara a servire l’interrupt

5. Sul secondo impulso INTA, il PIC disabilita il bus dati e si aspetta che la periferica abbassi la sua linea INT,

quindi si riporta nello stato iniziale

Facciamo l’assunzione che la maschera sia gestita internamente dal PIC: in fase di simulazione, forzeremo la

maschera a particolari valori per testarne le funzionalità. Nel nostro caso, la maschera non è selettiva come nel

protocollo reale, ma maschera soltanto gli interrupt maggiori del valore indicato (o nessuno).

3. Progettazione VHDL di basso livello

Il PIC è stato implementato in VHDL mediante componenti di più basso livello, applicando il principio di granularità di

VHDL: mentre per alcuni dei componenti è stata utilizzata una descrizione comportamentale del funzionamento,

essenzialmente dovuta alla laboriosità della progettazione in logica a due livelli, per altri sono invece state

implementate le effettive architetture fino al livello gate.

Per quanto riguarda la logica, abbiamo ritenuto interessante utilizzare la logica NOR. La scelta delle gate

normalmente è dettata dalla tecnologia su silicio attualmente in uso. Supponendo di lavorare in NMOS, sarebbe più

economico realizzare gate NOR. Le specifiche sulle gate vengono imposte dal produttore dell’hardware e/o dal

committente e non dal progettista, il quale si deve attenere ad esse. Grazie a VHDL, come vedremo, sarà possibile in

maniera molto semplice lo switch da una tecnologia all’altra cambiando semplicemente la dichiarazione delle

architetture nell’istanziazione dei componenti.

Siamo dunque partiti dalla progettazione degli elementi di più basso livello, utili in seguito a implementare i

principali componenti del PIC: abbiamo cioè implementato le gate NOR a due ingressi, e da lì le porte AND, OR e NOT

utilizzando le suddette NOR. Qui va fatta una precisazione doverosa: implementare le tre gate fondamentali per

mezzo di un’altra gate è un passaggio decisamente ridondante, in quanto il progettista ha gli strumenti per

effettuare direttamente la progettazione in una data logica di una rete. Tuttavia, poiché in alcuni punti, come ad

esempio nella maschera di interrupt, la funzionalità di più alto livello da implementare è una AND bit a bit, anziché

implementare tale funzionalità direttamente in NOR, abbiamo preferito aggiungere un layer di astrazione utilizzando

le entità AND2, che a loro volta sono mappate su entità NOR2. Nel caso fosse necessario passare ad una logica

NAND, basterebbe cambiare le istanze delle suddette AND2 verso una implementazione basata su NAND.

Tale comportamento non giustifica, tuttavia, l’utilizzo di tutto il set di gate nella progettazione delle reti più

complesse: per quanto ciò crei la massima astrazione e riuso possibile, una progettazione generica non è in grado di

ottimizzare il circuito logico per l’implementazione, aggiungendo livelli logici e ritardi che si potrebbero evitare

effettuando progettazioni separate per NAND e NOR anziché un’unica progettazione riusabile. Il motivo per cui noi lo

abbiamo fatto è stato però quello di mostrare, a titolo illustrativo, le potenzialità del linguaggio. Ma proprio in virtù

del potenziale di VHDL, qualunque progettista che metta mano ai nostri sorgenti potrà aggiungere, molto

semplicemente, una implementazione basata su NOR2 e/o su NAND2 dei medesimi componenti rispettandone

l’interfaccia e ottenendo un circuito ottimizzato.

Ciò premesso, analizziamo il contenuto del file NOR2.VHD realizzato con PeakVHDL. Questa libreria, realizzata

appositamente per questo progetto, contiene essenzialmente le porte logiche comuni e un registro D a 8 bit

asincrono implementato mediante flip-flop RS a NOR incrociate.

ENTITÀ NOR2

Dato che questa gate è per noi atomica, la implementiamo come behavioural, lasciando al produttore del circuito la

mappatura su transistor. Di seguito il codice sorgente:

entity NOR2 is

generic ( port_delay: time := 5ns );

port (

-- WIZ BEGINPORTS (Entity Wizard command)

x: in std_logic;

y: in std_logic;

o: out std_logic

-- WIZ ENDPORTS (Entity Wizard command)

);

end NOR2;

architecture BEHAVIOR of NOR2 is

-- Note: signals, components and other objects may be declared here if needed.

begin o <= x nor y after port_delay;

end BEHAVIOR;

ENTITÀ INV

Questa è una semplice porta NOT, realizzata implementando una singola gate NOR2 che in ingresso prende la stessa

variabile in entrambe le porte. Di seguito il sorgente:

entity inv is port ( x: in std_logic;

y: out std_logic

);

end inv;

architecture withnor2 of inv is

component n port (x, y: in std_logic; o: out std_logic);

end component;

for all: n use entity work.nor2(behavior);

begin g0: n port map (x,x,y);

end withnor2;

ENTITÀ OR2

La porta OR2 è semplicemente il negato della NOR2. Avremmo potuto realizzarla in maniera più esplicita con una

NOR e una NOT, ma abbiamo preferito ignorare il livello di astrazione offerto dalla NOT in favore di una più

comprensibile implementazione. Di seguito il sorgente:

entity or2 is port ( x, y: in std_logic;

o: out std_logic

);

end or2;

architecture withnor of or2 is

component n port (x, y: in std_logic; o: out std_logic); end component;

for all: n use entity work.nor2(behavior);

signal s1: std_logic;

begin g0: n port map (x, y, s1);

g1: n port map (s1, s1, o);

end withnor;

ENTITÀ AND2

Per realizzare una AND2 in NOR2, è sufficiente negare gli ingressi e mandarli a una NOR2. Di seguito il sorgente:

architecture WIRED of and2 is

component q1 port (x, y: in std_logic; o: out std_logic);

end component;

for all: q1 use entity work.nor2(BEHAVIOR);

signal i0, i1: std_logic;

begin g0: q1 port map(x, x, i0);

g1: q1 port map(y, y, i1);

g2: q1 port map(i0,i1,o);

end WIRED;

ENTITÀ RSFLIPFLOP

Per realizzare un flip-flop RS, ovviamente asincrono, è sufficiente incrociare due NOR tenendo presente che in uscita

alla NOR con ingresso S il bit in uscita è Q’. Di seguito il sorgente:

entity RSFLIPFLOP is

port ( r: in std_logic;

s: in std_logic;

q: buffer std_logic;

qn: buffer std_logic

);

end RSFLIPFLOP;

architecture WIRED of RSFLIPFLOP is

component N1 port (x, y: in std_logic; o: out std_logic);

end component;

for all: N1 use entity work.nor2(BEHAVIOR);

begin q0: N1 port map (r, qn, q);

q1: N1 port map (s, q, qn);

end WIRED;

ENTITÀ D_TO_RS

Questa entità è un po’ più complessa delle precedenti. Il suo funzionamento è quello di adattatore di interfaccia.

Non ha senso parlare di questa entità senza aver prima illustrato l’interfaccia di un flip-flop D, ma è necessario

implementarla prima di implementare il flip-flop D. Semplicemente, questo componente trasforma i segnali in

ingresso a un flip-flop D (D, Enable e Reset) in segnali di controllo per flip-flop RS. Di seguito il sorgente:

entity D_TO_RS is

port ( d: in std_logic;

enable: in std_logic;

reset: in std_logic;

r, s: out std_logic

);

end D_TO_RS;

architecture wired of D_TO_RS is

component iv port (x: in std_logic; y: out std_logic); end component;

component a2 port (x, y: in std_logic; o: out std_logic); end component;

component o2 port (x, y: in std_logic; o: out std_logic); end component;

for all: iv use entity work.inv(withnor2);

for all: a2 use entity work.and2(wired);

for all: o2 use entity work.or2(withnor);

signal i0,i1,i2,i3: std_logic;

begin g0: iv port map(reset, i0);

g1: a2 port map(i0, d, i1);

g2: o2 port map(enable, reset, i3);

g3: iv port map(i1, i2);

g4: a2 port map(i3, i2, r);

g5: a2 port map(i3, i1, s);

end wired;

ENTITÀ REGISTER8

Questa entità rappresenta un registro a 8 bit, costruito per mezzo di 8 flip-flop RS e relativi adattatori. Avremmo

potuto inglobare la coppia RS-adattatore in un flip-flop D, in modo da creare un nuovo componente riusabile ad un

più elevato livello di astrazione. Semplicemente, non lo abbiamo fatto.

entity REGISTER8 is

port ( d: in std_logic_vector(7 downto 0);

en, rst: in std_logic;

q, qn: buffer std_logic_vector(7 downto 0)

);

end REGISTER8;

architecture wired of REGISTER8 is

component ff port (r, s: in std_logic; q, qn: buffer std_logic); end component;

component ad port (d, enable, reset: in std_logic; r, s: out std_logic); end component;

for all: ff use entity work.rsflipflop(wired);

for all: ad use entity work.d_to_rs(wired);

signal res, set: std_logic_vector(7 downto 0);

begin flips: for i in 7 downto 0 generate

a : ad port map(d(i),en,rst,res(i),set(i));

f : ff port map(res(i),set(i),q(i),qn(i));

end generate;

end wired;

ENTITÀ THREESTATE

La porta tri-state è una speciale porta, non realizzabile come rete logica, la cui uscita ha 3 stati: alto, basso e alta

impedenza. Questo particolare stato rappresenta lo scollegamento elettrico del circuito a valle da quello a monte: la

porta, infatti, assume una impedenza tendente all’infinito tale che non vi possa essere influenza elettrica sul circuito

alla quale è collegata. La porta tri-state è utilizzata nei circuiti di scrittura su bus: quando più dispositivi devono

scrivere dati su di una stessa linea elettrica, in un dato istante deve essercene soltanto uno effettivamente connesso.

Se due dispositivi fossero in parallelo sulla linea di bus, potrebbe addirittura verificarsi un corto circuito. Per evitare

una simile situazione, è necessario l’uso di un interruttore che separi dal bus i dispositivi che non devono scrivervi. La

porta tri-state è proprio un interruttore elettronico, che funziona nel modo seguente:

 Se la linea di abilitazione è attiva, in uscita viene scritto il valore della linea dati

 Se la linea di abilitazione è disattiva, il circuito viene scollegato.

Non entreremo nei dettagli elettronici di come si realizzi una porta tri-state. In VHDL, l’abbiamo implementata per

mezzo di una descrizione data flow

entity threestate is

generic (port_delay: time:= 3ns);

port ( x: in std_logic;

en: in std_logic;

y: out std_logic

);

end threestate;

architecture dataflow of threestate is

begin y <= 'Z' when en = '0' else x;

end dataflow;

ENTITÀ RISING_EDGE_DETECTOR

Il riconoscitore di fronte di salita è un altro interessante dispositivo, che nel nostro caso è utilizzato per eseguire

correttamente la cattura di dati su un registro con rete asincrona. Il riconoscitore di fronte di salita restituisce uscita

alta solo sul fronte di salita di un segnale. L’uso che ne facciamo è particolare: nel PIC, è usato per limitare la durata

del segnale che abilita il registro buffer a catturare l’ID della periferica da scrivere sul bus, simulando un

comportamento impulsivo. Nel nostro caso, abbiamo appositamente modificato il circuito per far sì che il

riconoscitore di fronte sia in realtà un limitatore logico, ossia un circuito che limita il tempo in cui il segnale ad esso

in ingresso sia alto. Lo abbiamo progettato in logica NOR, e questo ci costa un ritardo di 10ns, tipicamente

inaccettabile per applicazioni come il riconoscimento di fronte. Tuttavia il circuito assolve correttamente i suoi

compiti, e solo il nome è un abuso di notazione.

Possiamo definire la relazione ingresso-uscita nel modo che segue:

Quando il segnale in ingresso è basso, l’uscita è comunque bassa. Quando il segnale in ingresso è alto, il

segnale in uscita è alto, ma la sua durata non supera i 30ns circa.

Ciò significa che la durata dell’impulso alto è pari al minimo tra 30ns e la durata dell’impulso alto in ingresso. Nella

realtà, i riconoscitori di fronte si realizzano con amplificatori operazionali retroazionati e non i circuiti logici. Il motivo

per cui facciamo durare l’impulso ben 30ns è dovuta ai ritardi sulle linee di traduzione dei segnali dei registri.

Di seguito il codice

entity rising_edge_detector is

-- Vincolo: il segnale X deve rimanere alto per almeno 30ns

port ( x: in std_logic;

y: out std_logic

);

end rising_edge_detector;

architecture wired of rising_edge_detector is

component nt port (x: in std_logic; y: out std_logic);

end component;

component nr port (x,y: in std_logic; o: out std_logic);

end component;

for all: nt use entity work.inv(withnor2);

for all: nr use entity work.nor2(behavior);

signal xn: std_logic;

signal stage: std_logic_vector(0 to 7);

begin n1: nt port map (x, xn);

n0: nt port map (x, stage(0));

sts: for i in 0 to 6 generate

ns: nt port map (stage(i), stage(i + 1));

end generate;

nf: nr port map (xn, stage(7),y);

end wired;

4. Implementazione dei componenti

Come già accennato, avremmo implementato il PIC per mezzo dei componenti principali. Innanzi tutto, abbiamo

definito l’architettura base del modello semplificato, dalla quale abbiamo rimosso il bus interno e registri come gli

ICW/OCW e l’ISR. Questa architettura è tutta sotto la direzione della logica di controllo, implementata attraverso

un’architettura VHDL comportamentale, che reagisce ai segnali provenienti dai componenti fisicamente mappati

all’interno del controller

Gli ingressi delle linee IR7-IR0 vengono presi direttamente dall’IRR, che li filtra in base alla maschera (nel nostro caso

impostata a 111, ma grazie al test bench IRR è facile desumere il comportamento del dispositivo con una maschera

attiva), quindi gli interrupt filtrati entrano nel priority resolver, che ha due variabili in uscita: una è un bit che indica

se è presente una richiesta di interrupt, l’altro un codice a 3 bit che indica l’ID del dispositivo a maggior priorità che

ha vinto l’arbitraggio.

Il codice va in ingresso ai bit meno significativi di un registro D, il quale, attraverso porte tri-state abilitate dalla logica

di controllo scrive sul bus una stringa i cui tre bit meno significativi rappresentano l’ID della periferica da servire. I bit

più significativi, per quanto non specificati nella logica, sono comunque impostati a zero.

La logica di controllo è guidata da due unici segnali in ingresso: INTA’ e il segnale is_int in uscita dal selettore di

priorità. È dunque realizzata mediante un processo VHDL.

Essi utilizzano alcune variabili di stato. Uno controlla quante pulsazioni INTA’ sono state ricevute, l’altro controlla lo

stato di funzionamento del protocollo di interrupt.

Quando il circuito è inizializzato, si suppone che le linee IR siano tutte a zero, dunque non vi siano richieste di

interruzione. Nel momento in cui almeno una delle linee IR si alza, il codice di interruzione cambia, e va in ingresso ai

registri di buffer che sono però ancora disabilitati. Nel contempo, il segnale is_int si alza e avvia un apposito processo

della logica di controllo che alza INT verso la CPU.

Supponendo che il processore riceva INT e reagisca, imporremo al test bench una pulsazione negativa sulla linea

INTA’. In seguito a questa prima pulsazione, il processo relativo alla linea INTA’ andrà ad abbassare INT e ad abilitare,

in sequenza, gli ingressi di abilitazione dei registri e quindi le porte tri-state del bus. Il motivo per cui la

memorizzazione del codice della periferica da servire è rimandato a questo punto del ciclo, è quello di permettere ad

una periferica di priorità maggiore di inserirsi nella coda di attesa dopo che una periferica di priorità minore ha già

inizializzato il ciclo e prendere il suo posto. Inoltre dobbiamo supporre che il tempo intercorso tra la ricezione delle

due pulsazioni di INTA’ sia sufficiente a scrivere i dati sul bus. Ricordiamo infatti che il PIC è un dispositivo asincrono,

e nelle specifiche sono definiti i tempi minimi.

entity PIC_8259A is

generic (reg_delay: time := 30ns; port_delay: time := 10ns);

port (

-- WIZ BEGINPORTS (Entity Wizard command)

Vcc: in std_logic;

GND: in std_logic;

CSn: in std_logic;

WRn: in std_logic;

RDn: in std_logic;

D: in std_logic_vector(7 DOWNTO 0);

CAS: inout std_logic_vector(3 downto 0);

SPnENn: inout std_logic;

INT: out std_logic;

IR: in std_logic_vector(7 DOWNTO 0);

INTAn: in std_logic;

A0: in std_logic

-- WIZ ENDPORTS (Entity Wizard command)

);

end PIC_8259A;

architecture COMPONENTI of PIC_8259A is

-- Note: signals, components and other objects may be declared here if needed.

component irr_reg port (IRQ: in std_logic_vector(7 downto 0); mask: in std_logic_vector(7 downto

0); int: out std_logic_vector(7 downto 0));

end component;

component imask port (level: in std_logic_vector(2 downto 0); mask: out std_logic_vector(7 downto

0)); end component;

component prio port (

irq: in std_logic_vector(7 downto 0);

int: out std_logic;

code: out std_logic_vector(2 downto 0)

); end component;

component reg8 port (

d: in std_logic_vector(7 downto 0);

en, rst: in std_logic;

q, qn: buffer std_logic_vector(7 downto 0)

);

end component;

component tstate port (

x: in std_logic;

en: in std_logic;

y: out std_logic

);

end component;


PAGINE

18

PESO

1.12 MB

PUBBLICATO

+1 anno fa


DETTAGLI
Corso di laurea: Corso di laurea in ingegneria informatica
SSD:
A.A.: 2013-2014

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher valeria0186 di informazioni apprese con la frequenza delle lezioni di Calcolatori Elettronici II e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Napoli Federico II - Unina o del prof Mazzeo Antonino.

Acquista con carta o conto PayPal

Scarica il file tutte le volte che vuoi

Paga con un conto PayPal per usufruire della garanzia Soddisfatto o rimborsato

Recensioni
Ti è piaciuto questo appunto? Valutalo!

Altri appunti di Calcolatori elettronici ii

Calcolatori Elettronici II – Gerarchia memorie
Dispensa
Calcolatori Elettronici II - Reti Sequenziali
Dispensa
Calcolatori Elettronici II
Dispensa
Calcolatori Elettronici II - Tesina
Appunto