57 / 63 · 概念
單一時脈 FIFO 的指標與邊界政策
實作深度 4 FIFO 的指標與 occupancy,明確定義 empty、full 下同時 push/pop 的接受條件。
單元可免費閱讀,註冊課程即可儲存學習進度。
不搬動所有資料,也能保持順序
FIFOFIFO是先進先出的佇列。滿、空,以及是否允許同時讀寫的規則,都是行為契約的一部分。 先進先出。單一時脈中,讀寫由同一上升緣決定。本課固定 DEPTH=4、資料寬 8 位元。Rptr 指下一個取出位置,wptr 指下一個放入位置,兩者各 2 位元,3 後自然回到 0。Occupancy 要表示存量 0~4,需 3 位元。
Rptr=wptr 在 empty、full 都可能成立,若只用地址相等判斷 empty,就會遺失滿載資料。因此另存數量 ,用 、 區分。深度 時,地址寬 與數量寬 為:
區分請求與接受的邊界政策
Push、pop 是請求。令 p、q 分別為實際接受的 push、pop,依非 reset 邊緣前的狀態計算:
Empty 下兩請求同來,沒有既有資料可 pop,因此只接受 push,新資料不會在該邊緣直接輸出。Full 下兩請求同來,可取出舊 head 釋放位置,因此兩者都接受。Full 表示「目前存四個」,在此政策中不是無條件禁止 push。
| 邊緣前狀態 | 只 push | 只 pop | push 與 pop |
|---|---|---|---|
| C=0 | 儲存一個 | 拒絕 | 只接受 push,C=1 |
| 0<C<4 | C+1 | C−1 | 兩者接受,C 不變 |
| C=4 | 拒絕 | C=3 | 兩者接受,C=4 |
前進時脈,檢查邊界
一開始同時請求 push、pop。因為是空的,只應接受 push。接著關 pop、填滿四格,再提供新值並開啟兩請求。佔用量保持 4,輸出為舊 head,儲存體留下新 tail。
四格 FIFO,初始為空,讀取與寫入指標皆為 0。非空時接受 pop;有空間或同一邊緣接受 pop 時,接受 push。
圖為上升緣上升緣是時脈由 0 變成 1 的瞬間。它與表示整段 CLK=1 期間的高準位不同。 詳細內容後狀態的教學模型。Pop 接受時 output valid=1;下一邊緣沒有接受 pop,輸出暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容值保持但 valid=0。空槽舊位元被隱藏。「重新開始」初始化整個練習,不表示 RTL 的記憶體重設重設將狀態回復至規格指定的初始值。必須一併定義它是同步或非同步,以及是否優先於其他控制訊號。 詳細內容行為。
含暫存輸出的 RTL
module fifo4 (
input logic clk, rst, push, pop,
input logic [7:0] din,
output logic [7:0] dout,
output logic dout_valid, full, empty,
output logic [2:0] occupancy
);
timeunit 1ns;
timeprecision 1ps;
logic [7:0] mem [0:3];
logic [1:0] rptr, wptr;
logic take_push, take_pop;
assign empty = (occupancy == 3'd0);
assign full = (occupancy == 3'd4);
assign take_pop = !rst && pop && !empty;
assign take_push = !rst && push && (!full || take_pop);
always_ff @(posedge clk) begin
if (rst) begin
rptr <= 2'd0;
wptr <= 2'd0;
occupancy <= 3'd0;
dout_valid <= 1'b0;
end else begin
dout_valid <= take_pop;
if (take_push) begin
mem[wptr] <= din;
wptr <= wptr + 2'd1;
end
if (take_pop) begin
dout <= mem[rptr];
rptr <= rptr + 2'd1;
end
case ({take_push, take_pop})
2'b10: occupancy <= occupancy + 3'd1;
2'b01: occupancy <= occupancy - 3'd1;
default: occupancy <= occupancy;
endcase
end
end
endmoduleOccupancy 沒有用兩個獨立 if 分別指定增減。若同時接受時對同一暫存器做兩次 nonblocking非阻塞賦值(nonblocking)在循序 RTL 中以 <= 表示。它先求右側運算式的值,再排程更新,讓同一邊緣的暫存器都能依據先前狀態計算。 詳細內容,最後一次會覆蓋前者。兩個指標各自在請求被接受時前進,數量則一次判斷四種情況。接受條件也防止從 0 減或從 4 加。
Full 時同址讀寫
依序放 A、B、C、D 後,rptr=wptr=0、occupancy=4。此時 pop=push=1、din=E,邊緣後 dout=A、dout_valid=1,邏輯內容為 [B,C,D,E]。兩指標變 1,數量保持 4。Mem[0] 雖更新為 E,dout 的 nonblocking 右側仍讀舊 A。此 read-first 碰撞行為是 full 替換政策必要條件。
下一步 push=0、pop=1,輸出 B、數量為 3。反之 empty 下 din=Z 且兩請求同來,只變成數量 1、dout_valid=0;需下個邊緣請求 pop 才輸出 Z。Empty 立即傳遞與 full 替換是不同設計選擇。
Rst 為高有效同步重設,初始化指標、數量與 dout_valid,不清記憶體與 dout。重設後即使殘留舊位元,empty 會阻止 pop,往後只讀新 push 寫過的位置,因此正常功能不需整個陣列重設。Dout_valid=0 時不得消費資料。Valid=1 是已接受 pop 的回應,輸出端沒有另設 ready,消費者必須接受。
DEPTH 不是 2 的次方時,不能直接沿用 2 位元自然溢位。例如深度 3 要明確從地址 2 返回 0。若合成使用 RAM,須確認同址碰撞支援 old-data。此程式只適用單一時脈,不能直接用於不同時脈 FIFO 的指標同步。
自己試試看
Reset 後逐邊緣給:(1) push A 與 pop、(2) 只 push B、(3) 只 push C、(4) 只 push D、(5) push E 與 pop、(6) 只 pop。列出各步後 queue、occupancy、dout_valid 與有效輸出,並解釋第五步為何需要 read-first。
查看解說
(1) [A],1,valid=0。(2) [A,B],2,valid=0。(3) [A,B,C],3,valid=0。(4) [A,B,C,D],4,valid=0。(5) [B,C,D,E],4,valid=1,dout=A。(6) [C,D,E],3,valid=1,dout=B。第一步無舊項目,拒絕 pop。第五步 rptr=wptr=0,同槽讀寫必須取舊 head A;write-first 若回 E 就破壞 FIFO 順序與內容。valid=0 的輸出值不列入比對。