Quando scrivo VHDL, consiglio vivamente di usare std_logic_vector (slv) invece di intero (int) per SIGNALS . (D'altra parte, usare int per generici, alcune costanti e alcune variabili può essere molto utile.) In poche parole, se dichiari un segnale di tipo int o devi specificare un intervallo per un numero intero, probabilmente stai facendo Qualcosa non va.
Il problema con int è che il programmatore VHDL non ha idea di quale sia la rappresentazione della logica interna di int, e quindi non possiamo trarne vantaggio. Ad esempio, se definisco un int compreso nell'intervallo da 1 a 10, non ho idea di come il compilatore codifichi questi valori. Speriamo che venga codificato come 4 bit, ma non sappiamo molto oltre. Se si potesse sondare i segnali all'interno dell'FPGA, potrebbe essere codificato da "0001" a "1010" o codificato da "0000" a "1001". È anche possibile che sia codificato in un modo che non ha assolutamente senso per noi umani.
Invece dovremmo semplicemente usare slv invece di int, perché allora abbiamo il controllo sulla codifica e abbiamo anche accesso diretto ai singoli bit. Avere accesso diretto è importante, come vedrai più avanti.
Potremmo semplicemente lanciare un int in slv ogni volta che abbiamo bisogno di accedere ai singoli bit, ma questo diventa davvero disordinato, molto veloce. È come ottenere il peggio di entrambi i mondi invece del meglio dei due mondi. Il codice sarà difficile da ottimizzare per il compilatore e quasi impossibile da leggere. Non lo consiglio.
Quindi, come ho detto, con slv hai il controllo sulle codifiche dei bit e l'accesso diretto ai bit. Quindi cosa puoi fare con questo? Ti mostrerò un paio di esempi. Diciamo che è necessario emettere un impulso una volta ogni 4.294.000.000 di orologi. Ecco come lo faresti con int:
signal count :integer range 0 to 4293999999; -- a 32 bit integer
process (clk)
begin
if rising_edge(clk) then
if count = 4293999999 then -- The important line!
count <= 0;
pulse <= '1';
else
count <= count + 1;
pulse <= '0';
end if;
end if;
end process;
E lo stesso codice usando slv:
use ieee.numeric_std.all;
signal count :std_logic_vector (32 downto 0); -- a 33 bit integer, one extra bit!
process (clk)
begin
if rising_edge(clk) then
if count(count'high)='1' then -- The important line!
count <= std_logic_vector(4293999999-1,count'length);
pulse <= '1';
else
count <= count - 1;
pulse <= '0';
end if;
end if;
end process;
Gran parte di questo codice è identico tra int e slv, almeno nel senso delle dimensioni e della velocità della logica risultante. Naturalmente uno conta e l'altro conta, ma questo non è importante per questo esempio.
La differenza sta nella "linea importante".
Con l'esempio int, questo comporterà un comparatore a 32 input. Con i LUT a 4 input utilizzati dallo Xilinx Spartan-3, ciò richiederà 11 LUT e 3 livelli di logica. Alcuni compilatori potrebbero convertirlo in una sottrazione che utilizzerà la catena di trasporto e estenderà l'equivalente di 32 LUT ma potrebbe funzionare più velocemente di 3 livelli di logica.
Con l'esempio slv, non esiste un confronto a 32 bit, quindi è "zero LUT, zero livelli di logica". L'unica penalità è che il nostro segnalino è un bit in più. Poiché la temporizzazione aggiuntiva per questo ulteriore bit di contatore è tutta nella catena di trasporto, c'è un ritardo di temporizzazione "quasi zero" aggiuntivo.
Naturalmente questo è un esempio estremo, poiché la maggior parte delle persone non userebbe un contatore a 32 bit in questo modo. Si applica ai contatori più piccoli, ma la differenza sarà meno drammatica sebbene ancora significativa.
Questo è solo un esempio di come utilizzare slv su int per ottenere tempi più rapidi. Ci sono molti altri modi per utilizzare slv: basta solo un po 'di immaginazione.
Aggiornamento: aggiunte cose per rispondere ai commenti di Martin Thompson sull'uso di int con "if (count-1) <0"
(Nota: suppongo che volessi dire "if count <0", poiché ciò renderebbe più equivalente alla mia versione SLV e rimuoverebbe la necessità di quella sottrazione aggiuntiva.)
In alcune circostanze, ciò potrebbe generare l'implementazione della logica prevista, ma non è garantito che funzioni sempre. Dipenderà dal tuo codice e da come il tuo compilatore codifica il valore int.
A seconda del compilatore e di come si specifica l'intervallo del proprio int, è del tutto possibile che un valore int di zero non codifichi in un vettore di bit "0000 ... 0000" quando lo fa nella logica FPGA. Perché la tua variazione funzioni, deve essere codificata in "0000 ... 0000".
Ad esempio, supponiamo che tu definisca un int per avere un intervallo da -5 a +5. Ti aspetti che il valore 0 sia codificato in 4 bit come "0000" e +5 come "0101" e -5 come "1011". Questo è il tipico schema di codifica a due complementi.
Ma non dare per scontato che il compilatore utilizzerà due complementi. Sebbene insolito, un complemento potrebbe portare a una logica "migliore". In alternativa, il compilatore potrebbe utilizzare una sorta di codifica "distorta" in cui -5 è codificato come "0000", 0 come "0101" e +5 come "1010".
Se la codifica di int è "corretta", probabilmente il compilatore dedurrà cosa fare con il bit di riporto. Ma se non è corretto, la logica risultante sarà orribile.
È possibile che l'utilizzo di un int in questo modo possa determinare dimensioni e velocità logiche ragionevoli, ma non è una garanzia. Passare a un altro compilatore (ad esempio da XST a sinossi) o passare a una diversa architettura FPGA potrebbe causare l'esatto errore.
Unsigned / Signed vs. slv è l'ennesimo dibattito. Puoi ringraziare il comitato del governo degli Stati Uniti per averci fornito così tante opzioni in VHDL. :) Uso slv perché è lo standard per l'interfaccia tra moduli e core. A parte questo, e alcuni altri casi nelle simulazioni, non penso che ci sia un enorme vantaggio nell'utilizzare slv rispetto a firmato / non firmato. Non sono inoltre sicuro che segnali firmati / non firmati supportino.