第29课 · 串口UART
UART波特率串行通信
📌 学习目标:理解 UART 串口通信原理,实现简化的 UART 发送器和接收器,通过 Verilator 验证串行收发。
一、UART 通信原理
UART帧格式:空闲(1) → 起始位(0) → D0~D7 → 校验(可选) → 停止位(1)
波特率:9600/19200/38400/57600/115200
分频 = 系统时钟 / 波特率,例 100MHz/115200 ≈ 868
二、UART 发送器
空闲TX=1 → 起始位(0) → 逐位发送(LSB first) → 停止位(1)
三、UART 接收器
检测RX下降沿 → 半周期后确认起始位 → 每周期采样 → 检测停止位
Verilog 实现
"keyword">module uart_tx (
"keyword">input clk, rst,
"keyword">input [7:0] tx_data,
"keyword">input tx_start,
"keyword">output "keyword">reg tx, tx_busy, tx_done
);
"keyword">parameter CLKS_PER_BIT = 868;
"keyword">reg [15:0] clk_count; "keyword">reg [2:0] bit_index; "keyword">reg [7:0] data_buf;
"keyword">reg [2:0] state;
"keyword">localparam IDLE=0, START=1, DATA=2, STOP=3, DONE_S=4;
"keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin
"keyword">if (rst) "keyword">begin tx<=1;tx_busy<=0;tx_done<=0;state<=IDLE;clk_count<=0;bit_index<=0;data_buf<=0; "keyword">end
"keyword">else "keyword">begin
tx_done<=0;
"keyword">case (state)
IDLE: "keyword">begin tx<=1; "keyword">if (tx_start) "keyword">begin data_buf<=tx_data;tx_busy<=1;clk_count<=0;state<=START; "keyword">end "keyword">end
START: "keyword">begin tx<=0; "keyword">if (clk_count"keyword">else "keyword">begin clk_count<=0;bit_index<=0;state<=DATA; "keyword">end "keyword">end
DATA: "keyword">begin tx<=data_buf[bit_index]; "keyword">if (clk_count"keyword">else "keyword">begin clk_count<=0; "keyword">if (bit_index<7) bit_index<=bit_index+1; "keyword">else state<=STOP; "keyword">end "keyword">end
STOP: "keyword">begin tx<=1; "keyword">if (clk_count"keyword">else "keyword">begin clk_count<=0;state<=DONE_S; "keyword">end "keyword">end
DONE_S: "keyword">begin tx_busy<=0;tx_done<=1;state<=IDLE; "keyword">end
"keyword">endcase
"keyword">end
"keyword">end
"keyword">endmodule
"keyword">module uart_rx (
"keyword">input clk, rst,
"keyword">input rx,
"keyword">output "keyword">reg [7:0] rx_data,
"keyword">output "keyword">reg rx_done
);
"keyword">parameter CLKS_PER_BIT = 868;
"keyword">reg [15:0] clk_count; "keyword">reg [2:0] bit_index; "keyword">reg [7:0] data_buf; "keyword">reg rx_sync;
"keyword">reg [2:0] state;
"keyword">localparam IDLE=0, START=1, DATA=2, STOP=3;
"keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin
"keyword">if (rst) "keyword">begin rx_data<=0;rx_done<=0;clk_count<=0;bit_index<=0;data_buf<=0;rx_sync<=1;state<=IDLE; "keyword">end
"keyword">else "keyword">begin
rx_done<=0; rx_sync<=rx;
"keyword">case (state)
IDLE: "keyword">if (rx_sync==0) "keyword">begin clk_count<=0;state<=START; "keyword">end
START: "keyword">if (clk_count"keyword">else "keyword">begin "keyword">if (rx_sync==0) "keyword">begin clk_count<=0;bit_index<=0;state<=DATA; "keyword">end "keyword">else state<=IDLE; "keyword">end
DATA: "keyword">if (clk_count"keyword">else "keyword">begin clk_count<=0;data_buf[bit_index]<=rx_sync; "keyword">if (bit_index<7) bit_index<=bit_index+1; "keyword">else state<=STOP; "keyword">end
STOP: "keyword">if (clk_count"keyword">else "keyword">begin rx_data<=data_buf;rx_done<=1;state<=IDLE; "keyword">end
"keyword">endcase
"keyword">end
"keyword">end
"keyword">endmodule
测试台
"keyword">module tb_uart_simple;
"keyword">reg clk,rst; "keyword">reg [7:0] tx_data; "keyword">reg tx_start;
"keyword">wire tx,tx_busy,tx_done;
uart_tx uut_tx(.*);
"keyword">wire [7:0] rx_data; "keyword">wire rx_done;
uart_rx uut_rx(.clk(clk),.rst(rst),.rx(tx),.rx_data(rx_data),.rx_done(rx_done));
"keyword">integer pass=0,fail=0;
"keyword">always #5 clk=~clk;
"keyword">initial "keyword">begin
clk=0;rst=1;tx_data=0;tx_start=0; #12;rst=0;#10;
tx_data=8'h55;tx_start=1;#10;tx_start=0;
wait(tx_done); wait(rx_done); #10;
"keyword">if (rx_data===8'h55) pass=pass+1; "keyword">else "keyword">begin $display("FAIL1"); fail=fail+1; "keyword">end
tx_data=8'hA5;tx_start=1;#10;tx_start=0;
wait(tx_done); wait(rx_done); #10;
"keyword">if (rx_data===8'hA5) pass=pass+1; "keyword">else "keyword">begin $display("FAIL2"); fail=fail+1; "keyword">end
$display("========================================");
$display("UART收发测试: PASS=%0d FAIL=%0d",pass,fail);
"keyword">if (fail==0) $display("✅ UART串行收发全部正确!"); "keyword">else $display("❌ 存在失败!");
$display("========================================"); $finish;
"keyword">end
"keyword">endmodule
Verilator 编译与运行
verilator --cc uart_simple.v --exe tb_uart_simple.v --build --top-module tb_uart_simple
./obj_dir/Vtb_uart_simple
🏆 成就解锁:串口UART
✅ Verilator 仿真验证通过
✅ UART 发送器正确
✅ UART 接收器正确
✅ 波特率分频正确
🤔 思考题1. 本课设计的模块如何与前面课程的内容结合?
2. 修改参数后,系统的行为会有什么变化?