54 / 63 · Concetto
Moltiplicatore unsigned shift-add multiciclo
Accumulare prodotti parziali in W clock verificando accettazione, fine, ultimo accumulo, larghezza e invariante.
Le lezioni sono gratuite. Iscriviti per salvare i progressi.
Prerequisiti: logica sequenziale, assegnazioni non bloccantiNonblocking Assegnazione RTL sequenziale con <=. Valuta il membro destro e pianifica l’aggiornamento, permettendo ai registri dello stesso fronte di usare lo stato precedente. Approfondisci, contatori, shift e sommatori.
Riutilizzare un sommatore su più clock
La moltiplicazione unsignedUnsigned Interpretazione dei bit come intero non negativo. Il campo a n bit è 0–2^n−1 e può differire dalla lettura signed degli stessi bit. Approfondisci a W bit somma i prodotti parziali scelti dai bit di B.
Sommarli tutti insieme richiede molta logica. Qui riutilizziamo un sommatore W volte, con un compromesso fra area e throughput. Un ciclo RTL da solo non genera una struttura multiciclo: servono registriRegistro Memorizza uno stato di più bit. I registri sincroni del corso acquisiscono gli ingressi specificati al fronte di clock. Approfondisci per lo stato intermedio e una condizione di fine.
Accumulatore P e moltiplicando spostato M hanno 2W bit; il moltiplicatore Q, spostato a destra, ne ha W. All'inizio P=0, M=zero-extend(A), Q=B. Ogni clock di lavoro aggiunge M a P se Q[0]=1, poi sposta M a sinistra e Q a destra di uno.
Dopo W passi Q=0 e P è il prodotto completo. Bastano 2W bit: e le somme parziali unsigned non superano il prodotto finale.
Con A=13, B=11, W=4, i bit bassi di Q sono 1,1,0,1. P segue 0→13→39→39→143; M usato è 13→26→52→104. Anche al terzo passo, Q[0]=0, shift e contatore devono avanzare.
Ogni colonna mostra lo stato dopo acquisizione o lavoro. La prima inizializza; le quattro successive calcolano. Al terzo passo resta fermo solo P, mentre M e Q scorrono.
Mostra i dati della forma d’onda
| Segnale | Forma d’onda | Valori del bus |
|---|---|---|
| edge | 23452 | capture → work1 → work2 → work3 → work4 |
| P after | 234.5 | 0 → 13 → 39 → 143 |
| M after | 23452 | 13 → 26 → 52 → 104 → 208 |
| Q after | 23452 | 11 → 5 → 2 → 1 → 0 |
| done after | 0...1 |
Definire prima accettazione e completamento
La richiesta viene accettata al fronte di salitaFronte di salita Passaggio del clock da 0 a 1, distinto dall’intero intervallo di livello CLK=1. Approfondisci con start=1 in idle. A t0 si salvano solo gli ingressi. Da t1 a tW si eseguono W passi; subito dopo tW product e done sono validi. Fra accettazione e fine passano W periodi, mentre contando anche il fronte iniziale compaiono W+1 fronti.
Con busy=1 start è ignorato. Anche il fronte finale non accetta una nuova richiesta, perché busy prima del fronte vale 1. La prima accettazione seguente è tW+1: distanza minima W+1 periodi. Tenendo start=1 si riparte al prossimo fronte idle. Il chiamante normalmente fornisce un impulso allineato all'accettazione.
module mul_iter #(parameter int W = 8) (
input logic clk, rst, start,
input logic [W-1:0] a, b,
output logic busy, done,
output logic [2*W-1:0] product
);
localparam int CW = (W <= 1) ? 1 : $clog2(W);
localparam logic [CW-1:0] LAST = CW'(W-1);
logic [CW-1:0] step;
logic [2*W-1:0] acc, m, next_acc;
logic [W-1:0] q;
assign next_acc = acc + (q[0] ? m : {2*W{1'b0}});
always_ff @(posedge clk) begin
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
product <= '0;
acc <= '0;
m <= '0;
q <= '0;
step <= '0;
end else begin
done <= 1'b0;
if (!busy) begin
if (start) begin
busy <= 1'b1;
acc <= '0;
m <= {{W{1'b0}}, a};
q <= b;
step <= '0;
end
end else begin
acc <= next_acc;
m <= m << 1;
q <= q >> 1;
if (step == LAST) begin
product <= next_acc;
busy <= 1'b0;
done <= 1'b1;
end else begin
step <= step + 1'b1;
end
end
end
end
endmoduleSi assume W≥1; CW è almeno 1 per evitare un contatore a zero bit con W=1. Reset sincrono active-high prevale sull'operazione. Cambiare a, b esterni dopo la richiesta non influisce su m, q salvati. product cambia solo al reset o al termine. done è un impulso di un ciclo, non un valid mantenuto fino al consumo.
L'ultima assegnazione è product<=next_acc, non product<=acc. Nonblocking legge i valori precedenti: usare acc perderebbe l'ultimo prodotto parziale. Analogamente next_acc usa Q[0] corrente anche scrivendo prima lo shift di q nel codice.
Invariante e scelte reali
Dopo k passi vale:
Separando Q in parte pari e bit basso si verifica che la relazione resta vera al passo successivo. A k=W, Q=0, dunque P=AB. L'invarianteInvariante Condizione sempre vera in ogni esecuzione ammessa. Una FIFO profonda quattro contiene, per esempio, sempre da zero a quattro elementi. Approfondisci spiega l'ordine di accumulo e shift meglio di poche forme d'onda.
Il cammino critico di un passo include sommatore a 2W bit e selezione d'ingresso. Eliminare * non garantisce area o velocità migliori. Confrontate DSP dedicati FPGA, sintesi e throughputThroughput Numero di operazioni accettate o completate per unità di tempo. Una pipeline può aumentarlo senza ridurre la latenza del singolo lavoro. Approfondisci richiesto. Non sono implementati early-exit, signed e backpressure del risultato. Il consumatore non deve perdere done.
Testate 0,1, massimo e B con solo il MSB acceso, oltre a start e cambi d'ingresso durante busy e annullamento tramite reset. Il solo MSB di B rivela particolarmente bene l'ultima accumulazione mancante.
Prova tu
W=4, A=9, B=10 sono accettati a t0. Calcolate acc, q, done dopo t1–t4 e la prima accettazione successiva. Cosa produce product<=acc finale?
Leggi la spiegazione
Inizialmente acc=0, m=9, q=1010. t1: bit basso 0, acc=0, q=0101, done=0. t2: aggiunge M=18, acc=18, q=0010, done=0. t3: bit basso 0, acc=18, q=0001, done=0. t4: aggiunge M=72, acc=90, q=0000, done=1, busy=0, product=90. Prima di t4 busy è ancora 1, quindi la prima richiesta successiva è t5. product<=acc legge il vecchio 18 e perde l’ultimo prodotto parziale 72.