22 / 24 · 實作
總整實作:FIFO 與兩級串流計算器
連接深度二的輸入 FIFO、兩個彈性級與計分板。
單元可免費閱讀,註冊課程即可儲存學習進度。
結合儲存、運算與交付
總整實作包含兩筆容量的輸入 FIFO 與兩個彈性運算級。第一級儲存九位元總和 a+b;第二級儲存 3×sum 對 256 取模的結果。標籤跟隨每個數值。總容量為四筆交易。背壓從輸出傳回,同時空槽仍可填入。假設單一時脈、暫存 FIFO、同時取出與推入,以及沒有輸出直通。
手動儲存 capstone-model.mjs 並執行 node capstone-model.mjs。在更新 FIFO 與暫存器前,先由舊狀態計算所有 ready 與下一級數值。JavaScript 陣列是功能佇列,不是可合成 HDL。
import assert from 'node:assert/strict';
const input = [[0,0],[1,2],[250,10],[255,255],
[128,128],[17,240],[90,90],[4,8]];
const q = [], expected = [], seen = [];
let s1 = null, s2 = null, issued = 0, delivered = 0;
for (let edge = 0; edge < 50; edge++) {
const ready = edge >= 25 || ![2,3,4].includes(edge % 7);
const old1 = s1, old2 = s2;
const r2 = old2 === null || ready;
const r1 = old1 === null || r2;
const pop = q.length > 0 && r1;
const inReady = q.length < 2 || pop;
if (old2 !== null && ready) {
assert.deepEqual(old2, expected.shift());
seen.push(old2.y);
delivered++;
}
const n2 = r2 ? (old1 === null ? null :
{ tag: old1.tag, y: old1.sum * 3 % 256 }) : old2;
const n1 = r1 ? (pop ?
{ tag: q[0].tag, sum: q[0].a + q[0].b } : null) : old1;
if (pop) q.shift();
if (issued < input.length && inReady) {
const [a,b] = input[issued];
q.push({ tag: issued, a, b });
expected.push({ tag: issued, y: (3*a + 3*b) % 256 });
issued++;
}
s1 = n1; s2 = n2;
if (old2 !== null && !ready) assert.deepEqual(s2, old2);
assert.ok(q.length <= 2);
assert.equal(issued - delivered,
q.length + Number(s1 !== null) + Number(s2 !== null));
}
assert.equal(issued, input.length);
assert.equal(delivered, input.length);
assert.deepEqual(seen, [0,9,12,250,0,3,28,36]);
assert.equal(expected.length + q.length, 0);
assert.equal(s1, null); assert.equal(s2, null);
console.log('CAPSTONE PASS: 8 ordered outputs, drained');| 標籤 | a,b | 寬位元結果 | y |
|---|---|---|---|
| 2 | 250,10 | 780 | 12 |
| 3 | 255,255 | 1530 | 250 |
| 5 | 17,240 | 771 | 3 |
第一筆輸入在邊緣 0 進入 FIFO,邊緣 1 進入 s1,邊緣 2 進入 s2。接收端在邊緣 2、3、4 停頓,使交付延至邊緣 5。無停頓延遲為三個邊緣;此交易需要五個。s2 保持時,其他空儲存空間仍可填入。
參考佇列只在接受時計算預期值。交付時比較標籤與結果,每個邊緣檢查占用量。邊緣 25 之後排空所有項目。Node 執行檢查八個有序結果;下列 SystemVerilog 檔案則獨立測試實際 FIFO 與管線暫存器。
建立並模擬 RTL
將 dp_stream_calculator.sv 與 tb_dp_stream_calculator.sv 下載至同一資料夾。無號八位元 a,b,tag 介面,以 in-valid/in-ready 接受,以 out-valid/out-ready 交付 y 與其標籤。九位元總和與十一位元縮放值,只在輸出縮窄。重設清空儲存,並抑制傳輸。
安裝 Icarus Verilog 後執行:
iverilog -g2012 -Wall -s tb_dp_stream_calculator -o dp-test.vvp dp_stream_calculator.sv tb_dp_stream_calculator.sv
vvp dp-test.vvp作者驗證已成功編譯並模擬這些檔案:
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3測試平台檢查算術、標籤、順序、停頓、滿時替換、指標回繞、四項重設取消與排空。改變停頓期間,並將延遲與無停頓三週期比較。這驗證 Icarus RTL 模擬,不代表合成、實體時序或 DDS 執行。
自己試試看
接收端永遠 ready 時,tag0 在哪個邊緣傳輸?若 FIFO 深度變為一,無停頓延遲與總容量如何變化?
查看解說
Tag0 在邊緣 3 傳輸,之前依序於 0 接受、1 進入 s1、2 進入 s2。保留暫存 FIFO,延遲仍為三個邊緣;容量減為 1+1+1=3 筆交易。修改 input-ready 深度限制,並重新檢查模型。