Table of Contents
Comprendere le operazioni aritmetiche in VHDL
VHDL (VHSIC Hardware Description Language) fornisce un supporto robusto per l'implementazione di operazioni aritmetiche, rendendolo un punto di riferimento per la progettazione di sistemi digitali come processori, processori di segnale digitale (DSP), e unità di controllo. Blocchi aritmetici come adder, subtrattori e moltiplicatori sono blocchi di costruzione fondamentali, e sapere come implementarli in modo efficiente è fondamentale per l'utilizzo sia per la simulazione e la sintesi.
Prima di immergersi in componenti specifici, è essenziale capire i due pacchetti VHDL chiave che permettono aritmetici: iee.numeric std] e eee.std logic unsigned]. L'approccio raccomandato per nuovi disegni è quello di utilizzare [FLT 4]
Tutti gli esempi di codice in questo articolo sono scritti utilizzando [IEEE 1076-2008[[] VHDL compatibile e i dispositivi di Xilinx o Intel (Altera) FPGA, ma i concetti si applicano a qualsiasi flusso di progettazione digitale.
Adders in VHDL
In VHDL, è possibile implementare adder a vari livelli di astrazione: comportamentale (utilizzando ]+[] operatore), flusso di dati (utilizzando l'assegnazione del segnale concomitante), o strutturale (constante componenti di basso livello di efficienza). Per la maggior parte progetti pratici, modellazione comportamentale con sintesi numerica std[
Semplice Aggiunta di Ondulazione
Una catena di scale a timone increspata insieme, dove il trasporto di ogni bit alimenta il riporto del prossimo bit più alto. L'esempio seguente mostra una scala a 4 bit senza segno increspata usando un processo comportamentale:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity adder4bit is
Port (
A : in unsigned(3 downto 0);
B : in unsigned(3 downto 0);
Sum : out unsigned(3 downto 0);
Cout : out std_logic
);
end adder4bit;
architecture Behavioral of adder4bit is
begin
process(A, B)
variable temp_sum : unsigned(4 downto 0);
begin
temp_sum := ('0' & A) + ('0' & B);
Sum <= temp_sum(3 downto 0);
Cout <= temp_sum(4);
end process;
end Behavioral;
Punti chiave:
- La concatenazione ('0' & A) estende gli ingressi a 5 bit, catturando il passaggio.
- Utilizzando unsigned[] direttamente con l'operatore []+[]] è sintetizzabile; lo strumento inferisce la logica appropriata della scala (ripple-carry, port-lookahead, o LUT-based in FPGAs).
- L'elenco della sensibilità del processo include tutti i segnali di input, garantendo gli aggiornamenti di uscita immediatamente su qualsiasi cambiamento (comportamento internazionale).
Aggiunta di carry-Lookahead (CLA)
[LT] [[[L]]] [[L]]] [[L]]] [[L]]] [[L]]] [[L]]] [[L]]] [[L'esempio] [[L]] [[L]]] [[L]]]]] [[L'esempio] [[L]]]] [[L'esempio]]]] [[L'esempio]]]]] [[[L'esempio]]]]]]] [[[[[L'insieme]]]]]]]]]]] [[[[[[[[[[[[[[[[[L'insieme]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]][[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
entity adder16bit is
Port (
A : in unsigned(15 downto 0);
B : in unsigned(15 downto 0);
Sum : out unsigned(15 downto 0);
CO : out std_logic
);
end adder16bit;
architecture Behavioral of adder16bit is
signal temp : unsigned(16 downto 0);
begin
temp <= ('0' & A) + ('0' & B);
Sum <= temp(15 downto 0);
CO <= temp(16);
end Behavioral;
Addetto con rilevamento overflow
Quando si utilizzano signed[]] numeri, il overflow si verifica quando il segno del risultato non corrisponde al segno previsto in base agli input.
entity signed_adder is
Port (
A : in signed(7 downto 0);
B : in signed(7 downto 0);
Sum : out signed(7 downto 0);
Overflow: out std_logic
);
end signed_adder;
architecture Behavioral of signed_adder is
signal extended_sum : signed(8 downto 0);
begin
extended_sum <= (A(7) & A) + (B(7) & B); -- sign extend
Sum <= extended_sum(7 downto 0);
Overflow <= extended_sum(8) XOR extended_sum(7); -- sign mismatch
end Behavioral;
Subtrasportatori in VHDL
La sottotrazione può essere implementata sia mediante l'uso diretto del ]-] operatore o aggiungendo il complemento dei due al minuend. Mentre la modellazione comportamentale è semplice, la comprensione della propagazione del prestito e la gestione dei risultati negativi è essenziale.
Subtrattore comportamentale diretto
Il subtrattore più semplice utilizza l'operatore - con tipi non firmati o non firmati. Per la sottotrazione non firmata, il risultato può diventare negativo se B > A; in tali casi è necessario rilevare un sottoflusso (borrow). L'esempio seguente restituisce sia la differenza che una bandiera di prestito:
entity subtractor4bit is
Port (
A : in unsigned(3 downto 0);
B : in unsigned(3 downto 0);
Diff : out unsigned(3 downto 0);
Borrow : out std_logic
);
end subtractor4bit;
architecture Behavioral of subtractor4bit is
signal temp_diff : signed(4 downto 0);
begin
process(A, B)
begin
temp_diff <= signed('0' & A) - signed('0' & B);
if temp_diff(4) = '1' then
Borrow <= '1';
else
Borrow <= '0';
end if;
Diff <= unsigned(temp_diff(3 downto 0));
end process;
end Behavioral;
Notare la conversione a ]firmato[] per il calcolo intermedio; questo permette una corretta gestione delle differenze negative.
Sottotrattore che utilizza il completamento di due
In alternativa, è possibile implementare la sottrazione aggiungendo il complemento di due di B. Questa tecnica è comune quando si riutilizza una scala esistente in un ALU. Il complemento di due di B è calcolato come (non B) + 1.
signal B_comp : unsigned(3 downto 0);
signal sum_with_borrow : unsigned(4 downto 0);
B_comp <= (not B) + 1; -- two's complement
sum_with_borrow <= ('0' & A) + ('0' & B_comp);
Diff <= sum_with_borrow(3 downto 0);
Borrow <= not sum_with_borrow(4); -- borrow asserted if carry out is 0
Entrambi i metodi sono di sintesi; selezionare quello che corrisponde alle preferenze architettoniche del vostro design.
Confronto e sottrazione
I sottraenti sono spesso utilizzati per implementare i comparatori. Esaminando il prestito o il segno della differenza, è possibile determinare se A > B, A < B, o A = B senza un comparatore dedicato. Ad esempio, dopo la sottrazione, se il risultato è zero (tutte le bit 0), gli input sono uguali. Se il prestito/segno è 1, allora A < B.
Moltiplicatori in VHDL
VHDL supporta l'operatore *[] per tipi non firmati e firmati, che inferisce un moltiplicatore combinato. Tuttavia, per larghezze di bit più grandi, i moltiplicatori combinati possono consumare logica significativa e avere ritardi di propagazione lunghi.
Multiplier combinato
Un moltiplicatore a 4 bit che utilizza l'operatore *[ è banale:
entity multiplier4bit is
Port (
A : in unsigned(3 downto 0);
B : in unsigned(3 downto 0);
Product : out unsigned(7 downto 0)
);
end multiplier4bit;
architecture Behavioral of multiplier4bit is
begin
Product <= A * B;
end Behavioral;
Questo fa riferimento ad un moltiplicatore combinato, che in un FPGA viene tipicamente implementato utilizzando fette DSP dedicate (come blocchi Xilinx DSP48) o logica basata su LUT. Per larghezze fino a 18 bit, la maggior parte degli strumenti FPGA possono mappare la moltiplicazione a una singola fetta DSP. Per moltiplicatori più ampi, lo strumento di sintesi può combinare più fette DSP o usare una logica morbida.
Multiplier sequenziale (Shift-and-Add)
Per i disegni contrattati nell'area o quando il ritardo combinato è inaccettabile, un moltiplicatore sequenziale che si può usare per i bit. L'algoritmo classico a turni e add moltiplica due numeri N-bit sui cicli N dell'orologio.
entity sequential_multiplier is
Port (
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
A : in unsigned(3 downto 0);
B : in unsigned(3 downto 0);
done : out std_logic;
Product : out unsigned(7 downto 0)
);
end sequential_multiplier;
architecture Behavioral of sequential_multiplier is
signal multiplicand : unsigned(7 downto 0);
signal multiplier : unsigned(3 downto 0);
signal product_reg : unsigned(7 downto 0);
signal count : integer range 0 to 4;
signal busy : std_logic;
begin
process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
count <= 0;
busy <= '0';
product_reg <= (others => '0');
done <= '0';
elsif start = '1' and busy = '0' then
multiplicand <= "0000" & A; -- left-aligned 4-bit multiplicand
multiplier <= B;
product_reg <= (others => '0');
count <= 0;
busy <= '1';
done <= '0';
elsif busy = '1' then
if multiplier(0) = '1' then
product_reg <= product_reg + multiplicand;
end if;
multiplicand <= multiplicand(6 downto 0) & '0'; -- shift left
multiplier <= '0' & multiplier(3 downto 1); -- shift right
count <= count + 1;
if count = 3 then
busy <= '0';
done <= '1';
end if;
end if;
end if;
end process;
Product <= product_reg;
end Behavioral;
Questo design utilizza un'aggiunta a L-bit per ciclo di clock (4 cicli per ingressi a 4 bit), che consente di risparmiare area, ma sacrifica la produttività e la latenza.
Pinza multiplier
Per applicazioni ad alto rendimento, un moltiplicatore conduttivo inserisce i registri tra le fasi della moltiplicazione combinata. Molti strumenti di sintesi FPGA possono condutturare automaticamente un moltiplicatore quando si aggiungono i registri delle tubazioni. Ad esempio, utilizzando un per ... generare loop o inserimento manuale della fase:
-- Pipelined unsigned 4x4 multiplier (2-stage pipeline)
architecture Pipelined of multiplier4bit is
signal stage1_prod : unsigned(7 downto 0);
signal stage1_A, stage1_B : unsigned(3 downto 0);
signal stage2_prod : unsigned(7 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
stage1_A <= A;
stage1_B <= B;
stage1_prod <= stage1_A * stage1_B; -- first stage
stage2_prod <= stage1_prod; -- second stage
Product <= stage2_prod;
end if;
end process;
end Pipelined;
Questo semplice approccio a due stadi raddoppia il throughput (un risultato per orologio dopo la latenza iniziale) aggiungendo un solo strato di registro in più.
Utilizzo delle DSP
In VHDL, utilizzando l'operatore *]] spesso automaticamente inferisce questi blocchi. Per garantire l'inferenza DSP, seguire le linee guida del fornitore: mantenere gli operandi all'interno della larghezza della fetta (ad esempio, 18x18, 18x25), evitare grandi operatori relazionali sul risultato, e utilizzare la larghezza appropriata
Ottimizzazione Tecniche e Considerazioni Sintesi
Quando si implementano operazioni aritmetiche in VHDL, diversi fattori influiscono sulla qualità dei risultati:
- Data Width:[] Usa la larghezza più piccola necessaria per ridurre la logica. Ad esempio, se gli input sono a 5 bit, usa unsigned(4 downto 0)].
- ]Attribuzioni Sintesi:[] Applicare attributi come ] ], use dsp, o mult style]] per influenzare la mappatura.
- Condivisione delle risorse:[] Se più operazioni utilizzano la stessa scala o moltiplicatore, considerare il riutilizzo dell'hardware tramite un componente condiviso o un singolo blocco aritmetico con ingressi multipli.
- Pipelining:[] Inserisci i registri per soddisfare i vincoli di tempismo. Per le catene additive, bilanciare il posizionamento del registro per evitare lunghi percorsi combinatori.
- Signed vs Unsigned:[]] Usa [[]]] [[] per operazioni firmate; lo strumento di sintesi tratterà la MSB come un bit di segno, che interessa la logica aritmetica inferredita.
- Cate di trasporto:[] Per gli adder più ampi, la catena di trasporto è una risorsa dedicata in FPGAs. Assicurare che il vostro strumento di sintesi non sta rompendo la catena utilizzando segnali intermedi inappropriati.
Combinazione delle operazioni: ALU Esempio
Per illustrare come gli adders, i sottraenti e i moltiplicatori si integrano in un design più ampio, si consideri una semplice unità di logica aritmetica (ALU) che può aggiungere, sottrarre o moltiplicare due valori a 8 bit basati su un segnale selezionato:
entity alu is
Port (
A, B : in signed(7 downto 0);
op : in std_logic_vector(1 downto 0); -- "00": add, "01": sub, "10": mul
result : out signed(15 downto 0)
);
end alu;
architecture Behavioral of alu is
begin
process(A, B, op)
begin
case op is
when "00" => result <= resize(A + B, 16); -- sign extend
when "01" => result <= resize(A - B, 16);
when "10" => result <= A * B;
when others => result <= (others => '0');
end case;
end process;
end Behavioral;
Questo ALU riutilizza lo stesso registro dei risultati e combina le tre operazioni. In sintesi, ogni operazione viene implementata come blocco separato, con l'output selezionato da un multiplexer. A seconda del dispositivo di destinazione, il moltiplicatore può essere il percorso critico.
Utilizzo di core IP per l'aritmetica complessa
Per operazioni avanzate (ad esempio, punto mobile, radice quadrata, modulo), o quando è necessario il massimo delle prestazioni, è consigliabile utilizzare core IP forniti dal fornitore. Questi sono altamente ottimizzati e hanno modelli di simulazione verificati. In VHDL, istanzia un core IP come componente, mappando i segnali alle sue porte.
- Xilinx Floating-Point Operator[[]] per aggiungere/sub/multiply/dividere in formato IEEE 754.
- Altera (Intel) ALTMULT ADD[[] per operazioni a più tempi.
- Divider di retice[] per la divisione a punto fisso.
Utilizzando i core IP minimizza il rischio e spesso si traduce in prestazioni migliori rispetto agli equivalenti codificati a mano.
Test e verifica
Scrivere le prove che esercitano i casi di angolo: overflow, zero, valori massimi e segni misti (per i tipi firmati). Per i moltiplicatori, testare tutte le combinazioni dei più piccoli input per verificare l'algoritmo. Utilizzare il assert]]] dichiarazione per controllare i risultati attesi. Esempio banco prova snippet per una scala a 4 bit:
signal A, B : unsigned(3 downto 0);
signal Sum : unsigned(3 downto 0);
signal Cout : std_logic;
...
A <= "1100"; B <= "0011"; wait for 10 ns;
assert (Sum = "1111" and Cout = '0')
report "Adder failed for 12 + 3" severity error;
Per i disegni più grandi, considerare l'utilizzo di stimoli casuali e modelli d'oro in linguaggi di scrittura (Python, Tcl) per generare vettori di prova.
Tecniche di banco di prova VHDL (SynthWorks)
Conclusioni
L'implementazione di operazioni aritmetiche in VHDL è una miscela di comprensione digitale aritmetica, uso competente di tipi di dati e operatori, e la consapevolezza del comportamento degli strumenti di sintesi.Aggiunti e sottraenti sono semplici quando si utilizza numeric std, mentre i moltiplicatori richiedono un'attenta considerazione delle prestazioni e dell'area.
Per ulteriori informazioni, consultare il Manuale di riferimento linguistico IEEE VHDL e la documentazione specifica del fornitore sull'inferenza aritmetica.