63 / 63 · 概念
綜合實作:UART 傳送器與資料框時序
結合分頻計數器、移位暫存器與流量控制建立 8N1 資料框,驗證位元邊界。
單元可免費閱讀,註冊課程即可儲存學習進度。
先固定資料框規格
先備知識:計數器、移位暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容、valid/ready 與同步重設重設將狀態回復至規格指定的初始值。必須一併定義它是同步或非同步,以及是否優先於其他控制訊號。 詳細內容。
UARTUART不使用獨立時脈線,而以起始位元、資料位元與停止位元進行序列通訊。收發雙方必須約定 baud rate 與訊框格式。 8N1 為一個起始位、八個資料位、無 parity、一個停止位。Idle、stop 為 1,start 為 0,資料從 LSBLSB是位權最低的位元。須同時確認表示方向與索引規則。 詳細內容 起送;每位維持相同長度。本練習只處理傳送,假設資料來源同時脈。
48 MHz 目標 115200 baud,可選整數分頻 D=417。實際約 115108 baud,誤差約 −0.080%。包含接收端的允許總誤差,須依裝置規格確認。
把 0x53 展成實際傳送順序
0x53 表示為 01010011,但線路從低位依序送 1,1,0,0,1,0,1,0。圖中一格不是一個系統時脈,而是 D 個時脈組成的一個位元期間。前後 idle 各畫一格供比較。
查看波形資料
| 訊號 | 波形 | 匯流排值 |
|---|---|---|
| tx | 101.0.10101. | |
| 階段 | 234523452345 | 閒置 → 起始 → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → 停止 → 閒置 |
Start 到 stop 結束恰為 10D 個時脈。連續的 1 或 0 仍各佔一個位元期間,沒有電位轉換不表示少了一位。
一個資料位元組只擷取一次
以下實作契約要求 CLKS_PER_BIT 為至少 1 的常數。Busy 時不接受輸入,只使用資料框開始時擷取的 data。不建立新 bit-clock,而是在原時脈內以計數器控制移動時點。
module uart_tx #(
parameter int unsigned CLKS_PER_BIT = 417,
localparam int CW = (CLKS_PER_BIT <= 1) ? 1 : $clog2(CLKS_PER_BIT)
) (
input logic clk, rst,
input logic in_valid,
output logic in_ready,
input logic [7:0] in_data,
output logic tx, busy, done
);
logic [9:0] frame;
logic [3:0] bits_left;
logic [CW-1:0] ticks_left;
assign in_ready = !busy;
always_ff @(posedge clk) begin
if (rst) begin
tx <= 1'b1;
busy <= 1'b0;
done <= 1'b0;
bits_left <= 4'd0;
ticks_left <= '0;
end else begin
done <= 1'b0;
if (!busy) begin
if (in_valid) begin
frame <= {1'b1, in_data, 1'b0};
tx <= 1'b0;
busy <= 1'b1;
bits_left <= 4'd10;
ticks_left <= CW'(CLKS_PER_BIT-1);
end
end else if (ticks_left != 0) begin
ticks_left <= ticks_left - 1'b1;
end else if (bits_left == 4'd1) begin
tx <= 1'b1;
busy <= 1'b0;
done <= 1'b1;
end else begin
frame <= {1'b1, frame[9:1]};
tx <= frame[1];
bits_left <= bits_left - 1'b1;
ticks_left <= CW'(CLKS_PER_BIT-1);
end
end
end
endmodule接受輸入的邊緣 E0,tx 變為 start=0,之後每隔恰好 D 個時脈切到下一位。E0+9D 開始 stop,E0+10D 解除 busy、產生一週期 done。最後的 stop 也必須維持 D 週期。
本實作在結束邊緣前 in_ready 都為 0,因此下一框最快到再下一邊緣接受,連續框的接受間隔為 個時脈。這個額外一時脈 idle 間隔也是正常行為的一部分。
手動預測位元序列
8'h53 從 LSB 起為 1,1,0,0,1,0,1,0,完整資料框如下。
| 順序 | start | d0 | d1 | d2 | d3 | d4 | d5 | d6 | d7 | stop |
|---|---|---|---|---|---|---|---|---|---|---|
| tx | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
驗證不能只計 tx 改變次數,因連續同值位元不會切換。以框起點為基準,取樣各位元區間中央並與預期比較,也檢查 start、stop 長度。快速單元驗證可用 D=1、2、3 等小值。
延伸設計:連接 FIFO
暫存讀取 FIFOFIFO是先進先出的佇列。滿、空,以及是否允許同時讀寫的規則,都是行為契約的一部分。 詳細內容 在 pop 接受邊緣後更新 dout、dout_valid,同時脈下級在再下一上升緣上升緣是時脈由 0 變成 1 的瞬間。它與表示整段 CLK=1 期間的高準位不同。 詳細內容取樣回應。若在 pop 接受的同一邊緣把 dout 送給 UART,可能是舊值。應加入保存回傳位元組的單格緩衝及 pending 狀態,在 UART ready 時恰好傳一次。
最終驗證包含 00、FF、01、80、55、53,busy 中改變的輸入、連續框,以及框中 reset。Reset 政策為中止傳送,tx 返回 idle=1;接收器仍可能看到有效框,因此僅憑 UART 框格式無法保證偵測到傳送中止。若必須丟棄受影響的資料,應明確約定系統行為:讓接收器的 reset 與傳送器連動,或向接收器提供獨立的中止訊號。
自己試試看
CLKS_PER_BIT=4,E0 接受資料。求 d0 起點、stop 起點與 done 邊緣。E40 有新的 in_valid=1,該邊緣會接受下一位元組嗎?
查看解說
D0 在 E4 後開始,stop 在 E36,done 在 E40 後。E40 前 busy=1、in_ready=0,新位元組尚未接受。傳送端須保持 valid、data,下一位元組可在 E41 接受。每框 40 個時脈,最小接受間隔 41 個。