54 / 63 · 概念
多週期 unsigned 移位加法乘法器
將部分積累加分成 W 週期,驗證接受與完成時點、最後累加、寬度及不變量。
單元可免費閱讀,註冊課程即可儲存學習進度。
先備知識:循序電路、nonblocking非阻塞賦值(nonblocking)在循序 RTL 中以 <= 表示。它先求右側運算式的值,再排程更新,讓同一邊緣的暫存器都能依據先前狀態計算。 詳細內容 指定、計數器、移位與加法器。
跨多個時脈重用一個加法器
Unsigned無符號(unsigned)將位元序列解讀為非負整數。n 位元範圍為 0 至 2^n−1;相同位元序列的帶符號解讀可能得到不同數值。 詳細內容 W 位元乘法,是乘數 B 各位元選擇的部分積總和。
同時加總所有部分積需要大量加法邏輯,此處改用一個加法器 W 次,以面積交換吞吐量。RTL 寫了迴圈不會自動成為多週期電路,還需跨時脈儲存計算狀態的暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容及完成條件。
累加器 P 為 2W 位元,左移被乘數 M 也為 2W,右移乘數 Q 為 W。開始時 P=0、M=zero-extend(A)、Q=B。每個工作時脈,若 Q[0]=1 就把 M 加入 P,並將 M 左移一位、Q 右移一位。
W 次工作後 Q=0,P 為完整乘積。2W 位元足夠,因最大值 小於 ,而 unsigned 部分積累加的中間 P 不會超過最終乘積。
例如 A=13,B=11,W=4,Q 最低位依序為 1,1,0,1;P 為 0→13→39→39→143,使用的 M 為 13→26→52→104。第三次 Q[0]=0 時,移位與計數器遞增仍必須執行。
每欄是接受請求或工作邊緣後的狀態。第一欄是計算前初始化,後四欄才是工作。第三次工作的舊 Q[0]=0,因此只有 P 保持,M、Q 仍移位。
查看波形資料
| 訊號 | 波形 | 匯流排值 |
|---|---|---|
| 邊緣 | 23452 | 擷取 → 運算 1 → 運算 2 → 運算 3 → 運算 4 |
| P 後 | 234.5 | 0 → 13 → 39 → 143 |
| M 後 | 23452 | 13 → 26 → 52 → 104 → 208 |
| Q 後 | 23452 | 11 → 5 → 2 → 1 → 0 |
| done 後 | 0...1 |
先定義請求與完成時點
下方介面在 idle 中於 start=1 的上升緣上升緣是時脈由 0 變成 1 的瞬間。它與表示整段 CLK=1 期間的高準位不同。 詳細內容接受請求。t0 只儲存輸入,從 t1 到 tW 計算 W 次,tW 後 product 與 done 有效。接受到完成為 W 個週期;若連起始邊緣一起數,則有 W+1 個邊緣。
Busy=1 時忽略 start。完成邊緣前 busy 仍為 1,因此也不接受新請求。下一請求最早 tW+1 接受,連續接受的最小間隔為 W+1 週期。若 start 一直為 1,返回 idle 後的下一邊緣又會開始。呼叫端通常依接受時點提供脈衝。
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
endmodule假設 W≥1,CW 至少為 1,避免 W=1 產生零位元計數器。Reset 是同步高有效,優先於進行中的運算。請求後外部 a,b 改變不影響結果,因使用儲存的 m,q 計算。Product 只在 reset 或完成時改變。Done 是一週期脈衝,不是保持至結果被消費的 valid。
最後指定為 product<=next_acc,而非 product<=acc。Nonblocking 右側讀更新前的值,若輸出 acc,會漏掉最後乘數位元的部分積。同理,即使 q 移位指定寫在前面,next_acc 仍依目前 Q[0] 計算。
計算不變量與實際設計選擇
完成 k 次工作後,下列關係保持成立。
把 Q 分成偶數部分與最低位,可確認下一步仍保留關係。k=W 時 Q=0,因此 P=AB。這個不變量不變量是在所有允許的執行過程中都必須成立的條件。例如,深度為 4 的 FIFO,其儲存數量必須一直介於 0 至 4。 詳細內容比只看小範例波形,更清楚說明累加與移位順序為何正確。
單次工作的關鍵路徑含 2W 位元加法器與輸入選擇。拿掉乘法符號,不代表一定更小或更快,還需比較 FPGA 專用 DSP、合成結果與所需吞吐量吞吐量是單位時間內可接受或完成的作業數。管線即使不縮短單筆作業的延遲,也能提高吞吐量。 詳細內容。此模組未實作提前結束、signed 處理或結果背壓,消費端必須遵守不漏掉 done 的契約。
驗證應涵蓋 0、1、最大值,以及只有最高位為 1 的 B;也需檢查 busy 中 start、輸入改變與 reset 取消運算。尤其最高位單一 1 的 B,能揭露最後一次累加遺漏。
自己試試看
W=4、A=9、B=10 在 t0 接受。求 t1~t4 後的 acc、q、done,並說明最早下一個接受邊緣。最後若寫 product<=acc,會輸出什麼?
查看解說
初始 acc=0、m=9、q=1010。t1:低位 0,acc=0、q=0101、done=0。t2:加 m=18,acc=18、q=0010、done=0。t3:低位 0,acc=18、q=0001、done=0。t4:加 m=72,acc=90、q=0000、done=1、busy=0,product=90。t4 前 busy=1,不接受 start,因此最快下次是 t5。若 product<=acc,nonblocking 讀到更新前 acc=18,漏掉最後部分積 72,輸出 18。