63 / 63 · 概念
综合实验:UART 发送器与帧时序
结合分频计数器、移位寄存器与流量控制,生成 8N1 帧并验证比特边界。
课时内容可免费阅读,选课后可保存学习进度。
先固定帧规格
先修知识:计数器、移位寄存器寄存器 存储多位状态的电路。本课程中的同步寄存器在时钟边沿存储指定输入。了解更多、valid/ready 与同步复位复位 将状态恢复为规定初值的控制。必须同时规定其同步或异步性质,以及相对于其他控制的优先级。了解更多。
UARTUART 无需独立时钟线,通过起始位、数据位和停止位进行串行通信的器件。收发双方必须约定波特率与帧格式。 的 8N1 格式包含一个起始位、八个数据位、无奇偶校验和一个停止位。idle、stop 为 1,start 为 0,数据从 LSBLSB 权重最低的比特。应同时检查表示方向与索引规则。了解更多 开始发送。所有比特保持相同时间。本实验只处理发送,假定数据提供方使用同一时钟。
在 48MHz 下以 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. | |
| phase | 234523452345 | idle → start → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → stop → idle |
从 start 到 stop 恰好占 10D 个时钟。连续的 1 或 0 仍各占一个比特周期,没有线路跳变并不意味着比特消失。
一个字节只捕获一次
以下实现约定 CLKS_PER_BIT 为不小于 1 的常数。busy 时不接受输入,只使用帧开始时捕获的 data。它不生成新的比特时钟,而是在原时钟内用计数器控制移位时刻。
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 开始停止位,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 个时钟。