Altifigence Academy

54 / 63 · 概念

多週期 unsigned 移位加法乘法器

將部分積累加分成 W 週期,驗證接受與完成時點、最後累加、寬度及不變量。

先備知識:循序電路、nonblocking非阻塞賦值(nonblocking)在循序 RTL 中以 <= 表示。它先求右側運算式的值,再排程更新,讓同一邊緣的暫存器都能依據先前狀態計算。 詳細內容 指定、計數器、移位與加法器。

跨多個時脈重用一個加法器

Unsigned無符號(unsigned)將位元序列解讀為非負整數。n 位元範圍為 0 至 2^n−1;相同位元序列的帶符號解讀可能得到不同數值。 詳細內容 W 位元乘法,是乘數 B 各位元選擇的部分積總和。

AB=∑i=0W−1bi(A2i)A B=\sum_{i=0}^{W-1} b_i(A2^i)

同時加總所有部分積需要大量加法邏輯,此處改用一個加法器 W 次,以面積交換吞吐量。RTL 寫了迴圈不會自動成為多週期電路,還需跨時脈儲存計算狀態的暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容及完成條件。

累加器 P 為 2W 位元,左移被乘數 M 也為 2W,右移乘數 Q 為 W。開始時 P=0、M=zero-extend(A)、Q=B。每個工作時脈,若 Q[0]=1 就把 M 加入 P,並將 M 左移一位、Q 右移一位。

Pk+1=Pk+Qk[0]Mk,Mk+1=2Mk,Qk+1=⌊Qk/2⌋P_{k+1}=P_k+Q_k[0]M_k,\quad M_{k+1}=2M_k,\quad Q_{k+1}=\lfloor Q_k/2\rfloor

W 次工作後 Q=0,P 為完整乘積。2W 位元足夠,因最大值 (2W−1)2(2^W-1)^2 小於 22W2^{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 仍移位。

以四次累加計算 13×11
查看波形資料
波形資料:每個字元表示一個區間;句點保持先前狀態;p 表示一個時脈週期。
訊號波形匯流排值
邊緣23452擷取 → 運算 1 → 運算 2 → 運算 3 → 運算 4
P 後234.50 → 13 → 39 → 143
M 後2345213 → 26 → 52 → 104 → 208
Q 後2345211 → 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 後的下一邊緣又會開始。呼叫端通常依接受時點提供脈衝。

SystemVerilog
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 次工作後,下列關係保持成立。

Pk+MkQk=ABP_k+M_kQ_k=A B

把 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。

此選擇適用於本瀏覽器,隨時可從頁尾變更。