Registri a scorrimento e contatori
In questa pagina 5
Dopo aver visto i registri semplici e il controllo del caricamento (Registri e microoperazioniUn registro a $n$ bit è formato da $n$ flip-flop (più porte) con clock comune; il caricamento si controlla con clock gating (OR con il clock: risparmia potenza ma introduce ritardi e clock skew) oppure, meglio, con load enable (MUX + D FF: $Q\leftarrow D$ se EN=1, $Q\leftarrow Q$ altrimenti). Un sistema digitale complesso si divide in datapath (registri e blocchi aritmetico-logici) e unità di controllo (FSM che invia i segnali di controllo e riceve i segnali di stato). Le microoperazioni, scritte in RTL (es. $K_1:R_2\leftarrow R_1$), sono di trasferimento, aritmetiche, logiche (con maschere: AND azzera, OR pone a 1, XOR complementa) e di scorrimento.Registri e microoperazioni →), si descrivono un esempio di trasferimento da più sorgenti e due registri molto usati: registri a scorrimento e contatori. (Versione sintetica: Registri e contatoriRegistro parallelo a n bit con caricamento abilitato; banco dei registri con due porte di lettura e una di scrittura; registri a scorrimento; contatori sincroni e asincroni (ripple), contatore modulo N con esempio svolto.Registri e contatori →.)
Trasferimento tra registri selezionati da un MUX
Un registro di destinazione può ricevere il contenuto da più sorgenti (altri registri, uscite di logica combinatoria). Con un trasferimento condizionato scritto in RTL come un if-then-else controllato da due segnali :
- se : è trasferito in , qualunque sia ;
- se e : è trasferito in ;
- se e : mantiene il valore (hold).
Un multiplexer (Multiplexer e funzioni logiche realizzate con decoder e multiplexerIl multiplexer (MUX) $2^n$-to-1 ha $2^n$ ingressi dati, $n$ ingressi di selezione e un'uscita che copia l'ingresso selezionato: $Y=\sum_i m_i(S),I_i$. Si realizza con decoder + AND di enable + OR (costo 22 per il 4-to-1) o direttamente (costo 18). Un MUX $2^n$-to-1 realizza qualunque funzione di $n$ variabili (ingressi dati = colonna della tabella di verità); con un MUX $2^{n-1}$-to-1 si usano le $n-1$ variabili come selezione e gli ingressi dati valgono $0$, $1$, $X$ o $\overline X$ (l'ultima variabile). I MUX a vettori selezionano gruppi di bit.Multiplexer e funzioni logiche realizzate con decoder e multiplexer →) sceglie il registro sorgente; il segnale () abilita il caricamento. Con sorgenti si usa un MUX -to-1: se i segnali di controllo sono linee 1-hot (una per sorgente), un encoder li trasforma nel codice binario di selezione (Decoder, encoder e priority encoderUn decoder $n$-to-$m$ ($m\le2^n$) converte un ingresso binario a $n$ bit in un'uscita 1-hot (un solo 1, nella posizione indicata): le sue uscite sono i mintermini degli ingressi, realizzati con $m$ AND; per decoder grandi si usa l'approccio gerarchico (costo in ingressi: 3-to-8 = 27, 6-to-64 = 182) e un enable. Ogni funzione = decoder + OR dei suoi mintermini. L'encoder fa l'operazione inversa (1-hot $\to$ binario) ma sbaglia con più ingressi a 1 o tutti a 0: il priority encoder risolve con una priorità e un'uscita V (valid).Decoder, encoder e priority encoder →); alcune sorgenti possono essere uscite di logica dedicata.
Registri a scorrimento (shift register)
Un registro a scorrimento è una catena di flip-flop D collegati in cascata (l'uscita di ciascuno è l'ingresso del successivo), tutti con lo stesso clock; a ogni fronte attivo i bit scorrono lateralmente di una posizione (a destra, a sinistra o in entrambe le direzioni).
Registro a 4 bit più semplice (solo FF): ingresso seriale SI = ingresso del primo FF; uscita seriale SO = uscita dell'ultimo; scorrimento a ogni fronte di salita.
Esempio. Shift register a 4 bit con che porta la sequenza (un bit per ciclo, partenza da ); il bit entra in e avanza verso :
| fronte | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | |
| 0001 | 0010 | 0101 | 1011 | 0110 | 1100 | 1000 | 0001 | 0011 | 0110 | |
| 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 |
Dopo 4 fronti il primo bit () arriva a : l'uscita seriale riproduce l'ingresso con un ritardo di 4 cicli di clock (una linea di ritardo).
Shift enable. Si può abilitare lo scorrimento passando il clock attraverso una porta controllata da (come per il ), ma con gli stessi problemi di clock skew: è preferibile un controllo sull'ingresso dei FF (multiplexer con hold).
Caricamento parallelo
Se si ha accesso all'ingresso di ogni FF (non solo del primo) si può caricare in parallelo il registro, e se si accede a tutte le uscite si possono leggere in parallelo i dati (anche inseriti in serie). Con questo si converte parallelo serie e serie parallelo.
Ogni stadio: tre AND, una OR e un D FF; due segnali di controllo, e (il clock va direttamente ai FF):
| operazione | ||
|---|---|---|
| 0 | 0 | nessun cambiamento (hold) |
| 0 | 1 | caricamento parallelo dei |
| 1 | X | scorrimento (da verso ) |
In RTL: ( entra in , ogni , cioè verso il MSB) e ; l'hold è implicito quando nessuna condizione è soddisfatta. Lo scorrimento ha priorità sul caricamento (vale con indipendentemente da ).
Per lo scorrimento nella direzione opposta (dal MSB verso il LSB) il circuito è identico con gli indici di e in ordine inverso: .
Convenzione. = shift a sinistra = i bit vanno verso il MSB (con disegnato a sinistra, dal LSB "da destra a sinistra"); = shift a destra = i bit vanno verso il LSB ("da sinistra a destra", dal MSB al LSB). Se il comando è i bit scorrono da sinistra a destra a prescindere dal valore di .
Shift register bidirezionale (universale)
Si aggiunge a ogni stadio un multiplexer 4-to-1 con selezione :
| operazione | ||
|---|---|---|
| 0 | 0 | nessun cambiamento (hold) |
| 0 | 1 | shift left |
| 1 | 0 | shift right |
| 1 | 1 | caricamento parallelo |
Ingressi seriali: LSI (left serial input, entra in nello shift left) e RSI (right serial input, entra in nello shift right); uscite seriali: per lo shift left, per lo shift right. In RTL: ; ; ; l'hold per è implicito.
Usi
- Interfacce seriali: per trasferire bit da un trasmettitore a un ricevitore distante usare linee è costoso (area, ritardo, consumo, rumore); con uno shift register si usa una sola linea: il trasmettitore carica i dati in parallelo, i bit viaggiano in serie, il ricevitore li riceve in serie in uno shift register e, quando sono arrivati tutti, li legge in parallelo.
- Moltiplicazione (somma di prodotti parziali traslati, Sommatori binari - half adder, full adder e ripple carryHalf adder (2 ingressi): $S=X\oplus Y$, $C=XY$. Full adder (3 ingressi, con riporto in ingresso $Z$): $S=X\oplus Y\oplus Z$, $C=XY+XZ+YZ=G+PZ$ con $P=X\oplus Y$, $G=XY$; si realizza con due half adder e una OR. Il ripple carry adder a $n$ bit concatena $n$ full adder: il riporto "ondeggia" dal LSB al MSB, quindi il ritardo cresce linearmente con $n$. È un circuito iterativo (gerarchico e regolare). Il moltiplicatore a 2 bit usa 4 AND e 2 half adder.Sommatori binari - half adder, full adder e ripple carry →).
- Generatore di numeri pseudo-casuali: si porta in ingresso lo XOR di alcuni bit del registro, ottenendo una sequenza pseudo-casuale.
- Linea di ritardo: il segnale arriva con ritardo scelto, cambiando la frequenza del clock o l'uscita da cui si preleva.
Contatori
Un contatore è un registro che, in risposta a una serie di ingressi (il clock), attraversa una sequenza predefinita di stati (numeri binari, sequenza casuale...), fornendo in uscita un conteggio. Un contatore binario a bit conta da a . Due categorie: ripple (asincrono) e sincrono.
Contatore ripple
Una catena di FF D le cui uscite danno il numero binario. Il FF del LSB riceve il clock di sistema; ogni FF successivo riceve come clock l'uscita negata del precedente; l'ingresso di ogni FF è collegato alla sua uscita negata : al fronte di salita del proprio clock ogni FF cambia stato (toggle). Un reset asincrono azzera il registro.
Funzionamento: commuta a ogni fronte del clock di sistema; commuta ogni volta che passa da 1 a 0 (cioè quando sale); ogni volta che scende ; e così via. Partendo da : dopo il primo fronte (); dopo il secondo e (); la sequenza è .
Il tempo di transizione tra due conteggi dipende dal numero di FF: quando il conteggio passa da a i flip-flop cambiano uno alla volta in cascata, e per un tempo pari a le uscite attraversano valori intermedi: . Letture fatte in questo intervallo danno valori sbagliati.
Contatore all'indietro: se il clock di ogni FF è l'uscita diretta (non negata) del precedente, si ottiene un conteggio verso il basso ( commuta quando sale ).
Pro e contro. Struttura semplicissima; ma è un circuito asincrono: i bit di uscita hanno ritardi diversi rispetto al clock di ingresso, tanto più quanti più bit ci sono (problemi nella tempistica di lettura). Si usa dove non ci sono vincoli stringenti di velocità: clock a bassa frequenza o eventi rari come clock.
Contatore sincrono binario
Tutti i flip-flop sono sensibili allo stesso clock: commutano insieme, non in cascata.
Contatore sincrono binario seriale. Ingresso (abilita il conteggio) e uscita (carry output, permette di costruire contatori a più stadi). Con , a ogni fronte il valore memorizzato è incrementato di 1. Ogni XOR somma 0 o 1 al bit memorizzato (il bit commuta quando tutti i bit inferiori valgono 1), ogni AND propaga il segnale allo stadio successivo. Il ritardo di propagazione è quello di 4 AND per e di 3 AND più una XOR per .
Contatore sincrono parallelo. Sostituendo la catena di AND a 2 ingressi con AND in parallelo a più ingressi si ottiene un contatore più veloce. Collegando del primo di due contatori a 4 bit a del secondo si ottiene un contatore a 8 bit.
Rispetto al ripple, il contatore sincrono ha una struttura più complessa ma è più veloce e meno soggetto a problemi di temporizzazione.
VHDL
Registro a bit con load sincrono e clear asincrono attivo basso (parametro generic):
library IEEE;
use IEEE.std_logic_1164.all;
entity register_wLoad is
generic ( n : integer := 8 );
port ( clk, clear_n, load : in std_logic;
D : in std_logic_vector(n-1 downto 0);
Q : out std_logic_vector(n-1 downto 0) );
end register_wLoad;
architecture beh of register_wLoad is
begin
process (clk, clear_n)
begin
if clear_n = '0' then
Q <= (others => '0');
elsif clk'event and clk = '1' then
if load = '1' then Q <= D; end if; -- load = '0': hold
end if;
end process;
end beh;Shift register a 4 bit con reset asincrono (ingresso seriale SI in , uscita seriale SO ):
entity ShiftRegister_4_r is
port ( CLK, RST, SI : in std_logic;
SO : out std_logic;
Q : out std_logic_vector(3 downto 0) );
end ShiftRegister_4_r;
architecture beh of ShiftRegister_4_r is
signal shift : std_logic_vector(3 downto 0);
begin
process (RST, CLK)
begin
if RST = '1' then
shift <= "0000";
elsif CLK'event and CLK = '1' then
shift <= shift(2 downto 0) & SI; -- scorrimento verso il MSB
end if;
end process;
Q <= shift;
SO <= shift(3);
end beh;Contatore a 4 bit con enable, reset asincrono e carry-out:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity Counter_4_r is
port ( CLK, RST, EN : in std_logic;
Q : out std_logic_vector(3 downto 0);
CO : out std_logic );
end Counter_4_r;
architecture beh of Counter_4_r is
signal count : std_logic_vector(3 downto 0);
begin
process (RST, CLK)
begin
if RST = '1' then
count <= "0000";
elsif CLK'event and CLK = '1' then
if EN = '1' then
count <= std_logic_vector(unsigned(count) + 1);
end if;
end if;
end process;
Q <= count;
CO <= '1' when (EN = '1' and count = "1111") else '0';
end beh;Con il conteggio avanza di uno a ogni fronte di salita e diventa 1 quando il conteggio vale ; con il conteggio è fermo. Nel testbench si applica il reset iniziale, poi per 17 cicli (per vedere il ritorno a ), poi .
Errori comuni
- Confondere ritardo del ripple (cresce con ) con quello del sincrono (cresce molto meno).
- Scambiare le direzioni /: va verso il MSB.
- Credere che nello shift register con e vinca il caricamento: vince lo scorrimento.
- Dimenticare che nel ripple le uscite sono errate durante la propagazione.
- Usare le uscite di un ripple counter come se fossero tutte sincrone.
Versione ripasso
- Sorgenti multiple: MUX sceglie la sorgente (; ; altrimenti hold); encoder se i controlli sono 1-hot.
- Shift register: catena di FF; SI in , SO ; ritardo di 4 cicli (ingresso dopo 4 fronti). Con load parallelo: (verso MSB, priorità su Load), , hold. : verso il LSB. Bidirezionale: MUX 4-to-1, hold, shift left, shift right, load; LSI/RSI.
- Usi: serie/parallelo, interfacce, LFSR, linee di ritardo, moltiplicazione.
- Contatore ripple (asincrono): clock di ogni FF = del precedente, (toggle); ritardo (); all'indietro con diretta. Sincrono: stesso clock, XOR + AND (serie/parallelo), e (cascata: ); più veloce.
- VHDL:
register_wLoad(generic ,clear_n,load),shift <= shift(2 downto 0) & SI, contatore conunsigned(count)+1eCO(VHDL - latch, flip-flop e reset sincrono e asincronoIn VHDL un elemento di memoria si descrive con un process il cui ramo non assegna sempre il segnale: latch D = process(D,C) con if C='1' then Q<=D; flip-flop positive-edge-triggered = process(CLK) con if CLK'event and CLK='1'. Reset asincrono: RST nella sensitivity list e testato per primo (if RST='1' ... elsif fronte); reset sincrono: solo CLK in lista e RST testato dentro al ramo del fronte (serve un fronte per averne effetto). Il testbench di un circuito sincrono ha un process che genera il clock e uno che applica gli ingressi lontano dal fronte attivo.VHDL - latch, flip-flop e reset sincrono e asincrono →). - Errori: /; Load vs Shift; ripple considerato sincrono.