[RTLLM-p020] Memory/FIFO (First-In, First-Out)/asyn_fifo
參數化非同步 FIFO
題目說明
請設計一個名為 asyn_fifo 的參數化非同步 FIFO,用於在兩個不同時脈域之間安全地傳輸資料。
FIFO 的寫入端使用 wclk,讀取端使用 rclk。由於兩個時脈彼此獨立,讀寫指標不可直接跨時脈域傳遞,必須先轉換為 Gray code,再使用兩級正反器同步器傳遞至另一個時脈域。
FIFO 必須包含以下部分:
- 雙埠 RAM
- 寫入指標與 Full 控制邏輯
- 讀取指標與 Empty 控制邏輯
- 讀指標到寫時脈域的兩級同步器
- 寫指標到讀時脈域的兩級同步器
模組參數
| 參數 | 預設值 | 說明 |
|---|---|---|
WIDTH |
8 | 每筆資料的位元寬度 |
DEPTH |
16 | FIFO 可儲存的資料筆數 |
本題要求 DEPTH 必須是 2 的冪次,例如:
2, 4, 8, 16, 32, 64, ...
FIFO 的位址寬度為:
ADDR_WIDTH = $clog2(DEPTH)
讀寫指標比 RAM 位址多一個位元:
PTR_WIDTH = ADDR_WIDTH + 1
例如,當:
DEPTH = 8
則:
ADDR_WIDTH = 3
PTR_WIDTH = 4
因此使用 4 位元 Gray code 指標,低 3 位元對應 RAM 位址。
模組介面
| 埠 | 方向 | 位元寬度 | 說明 |
|---|---|---|---|
wclk |
input | 1 | 寫入端時脈 |
rclk |
input | 1 | 讀取端時脈 |
wrstn |
input | 1 | 寫入端低電位有效非同步重置 |
rrstn |
input | 1 | 讀取端低電位有效非同步重置 |
winc |
input | 1 | 寫入請求 |
rinc |
input | 1 | 讀取請求 |
wdata |
input | WIDTH |
寫入資料 |
wfull |
output | 1 | FIFO 已滿 |
rempty |
output | 1 | FIFO 為空 |
rdata |
output | WIDTH |
讀出資料 |
雙埠 RAM
請建立一個名為 dual_port_RAM 的子模組。
模組介面如下:
module dual_port_RAM #(
parameter DEPTH = 16,
parameter WIDTH = 8
) (
input wclk,
input wenc,
input [$clog2(DEPTH)-1:0] waddr,
input [WIDTH-1:0] wdata,
input rclk,
input renc,
input [$clog2(DEPTH)-1:0] raddr,
output reg [WIDTH-1:0] rdata
);
RAM 必須具有:
- 獨立寫入時脈
- 獨立讀取時脈
- 同步寫入
- 同步讀取
- 可參數化深度與資料寬度
只有在:
wenc = 1
時才可寫入 RAM。
只有在:
renc = 1
時才更新 rdata。
寫入操作
真正的 RAM 寫入使能為:
wenc = winc AND NOT wfull
因此,當 FIFO 已滿時,即使 winc 為 1,寫入指標與 RAM 內容也不可改變。
寫入二進位指標的下一個值為:
wbin_next = wbin + wenc
再將二進位指標轉換為 Gray code:
wgray_next = (wbin_next >> 1) XOR wbin_next
RAM 寫入位址使用二進位指標的低 ADDR_WIDTH 位元:
waddr = wbin[ADDR_WIDTH-1:0]
讀取操作
真正的 RAM 讀取使能為:
renc = rinc AND NOT rempty
因此,當 FIFO 為空時,即使 rinc 為 1,讀取指標也不可改變。
讀取二進位指標的下一個值為:
rbin_next = rbin + renc
再轉換為 Gray code:
rgray_next = (rbin_next >> 1) XOR rbin_next
RAM 讀取位址使用二進位指標的低 ADDR_WIDTH 位元:
raddr = rbin[ADDR_WIDTH-1:0]
Gray Code
二進位轉 Gray code 的公式為:
gray = binary XOR (binary >> 1)
Gray code 在每次指標增加時通常只有一個位元發生變化,因此適合用於跨時脈域同步。
不可直接將多位元二進位指標跨時脈域同步,因為多個位元可能同時變化,接收端可能取得不一致的指標值。
指標同步器
讀指標同步至寫時脈域
讀取 Gray code 指標必須經過兩級 wclk 正反器同步後,才能送至 Full 判斷邏輯。
rptr
│
▼
第一級同步器
│
▼
第二級同步器
│
▼
rptr_sync_wclk
寫指標同步至讀時脈域
寫入 Gray code 指標必須經過兩級 rclk 正反器同步後,才能送至 Empty 判斷邏輯。
wptr
│
▼
第一級同步器
│
▼
第二級同步器
│
▼
wptr_sync_rclk
Empty 判斷
當下一個讀取 Gray code 指標等於同步後的寫入 Gray code 指標時,FIFO 為空:
rempty_next = (rgray_next == wgray_sync)
Full 判斷
當下一個寫入指標比讀取指標多繞行一圈時,FIFO 為滿。
對標準 Gray code FIFO 而言,Full 判斷方式是:
- 將同步後讀指標的最高兩個位元反相。
- 其餘位元保持不變。
- 與下一個寫入 Gray code 指標比較。
wfull_next =
wgray_next ==
{NOT rgray_sync[MSB:MSB-1],
rgray_sync[MSB-2:0]}
例如,當 DEPTH = 8 時,指標寬度為 4:
wgray_next[3:0] ==
{
~rgray_sync[3:2],
rgray_sync[1:0]
}
重置行為
wrstn 與 rrstn 都是低電位有效非同步重置。
當 wrstn = 0 時:
- 寫入二進位指標歸零
- 寫入 Gray code 指標歸零
- 讀指標同步器歸零
wfull歸零
當 rrstn = 0 時:
- 讀取二進位指標歸零
- 讀取 Gray code 指標歸零
- 寫指標同步器歸零
rempty設為1
設計要求
- 模組名稱必須為
asyn_fifo。 - 必須支援參數
WIDTH與DEPTH。 DEPTH必須是 2 的冪次。- 必須實例化
dual_port_RAM子模組。 - 讀寫時脈必須彼此獨立。
- 跨時脈域只能同步 Gray code 指標。
- 每個跨時脈域指標必須使用兩級正反器同步。
- FIFO 為滿時不可寫入。
- FIFO 為空時不可讀取。
wfull必須在寫時脈域產生。rempty必須在讀時脈域產生。
程式設計模板
module dual_port_RAM #(
parameter DEPTH = 16,
parameter WIDTH = 8
) (
input wclk,
input wenc,
input [$clog2(DEPTH)-1:0] waddr,
input [WIDTH-1:0] wdata,
input rclk,
input renc,
input [$clog2(DEPTH)-1:0] raddr,
output reg [WIDTH-1:0] rdata
);
// Write your code here
endmodule
module asyn_fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16
) (
input wclk,
input rclk,
input wrstn,
input rrstn,
input winc,
input rinc,
input [WIDTH-1:0] wdata,
output wfull,
output rempty,
output [WIDTH-1:0] rdata
);
// Write your code here
endmodule
評論