文档
SystemVerilog 进阶:UART 发送器 + 接收器(可综合)
目标
用 SystemVerilog 实现完整的 UART 收发器(8N1, 115200bps),可直接综合到 FPGA。
UART 发送器
// uart_tx.sv
module uart_tx #(parameter CLKS_PER_BIT = 434) (
input logic clk, rst_n,
input logic [7:0] tx_data,
input logic tx_valid,
output logic tx_ready,
output logic tx
);
typedef enum logic [2:0] {S_IDLE, S_START, S_DATA, S_STOP} state_t;
state_t state = S_IDLE;
logic [15:0] clk_cnt = 0;
logic [2:0] bit_idx = 0;
logic [7:0] data_reg = 0;
assign tx_ready = (state == S_IDLE);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE; tx <= 1'b1;
clk_cnt <= 0; bit_idx <= 0;
end else begin
case (state)
S_IDLE: begin
tx <= 1'b1;
if (tx_valid) begin
data_reg <= tx_data;
state <= S_START;
clk_cnt <= 0;
end
end
S_START: begin
tx <= 1'b0; // 起始位 = 低
if (clk_cnt >= CLKS_PER_BIT - 1) begin
clk_cnt <= 0; bit_idx <= 0;
state <= S_DATA;
end else clk_cnt <= clk_cnt + 1;
end
S_DATA: begin
tx <= data_reg[bit_idx];
if (clk_cnt >= CLKS_PER_BIT - 1) begin
clk_cnt <= 0;
if (bit_idx >= 7) state <= S_STOP;
else bit_idx <= bit_idx + 1;
end else clk_cnt <= clk_cnt + 1;
end
S_STOP: begin
tx <= 1'b1; // 停止位 = 高
if (clk_cnt >= CLKS_PER_BIT - 1) begin
state <= S_IDLE; clk_cnt <= 0;
end else clk_cnt <= clk_cnt + 1;
end
endcase
end
end
endmodule
UART 接收器
// uart_rx.sv
module uart_rx #(parameter CLKS_PER_BIT = 434) (
input logic clk, rst_n,
input logic rx,
output logic [7:0] rx_data,
output logic rx_valid,
output logic rx_error
);
typedef enum logic [1:0] {S_IDLE, S_START, S_DATA, S_STOP} state_t;
state_t state = S_IDLE;
logic [15:0] clk_cnt = 0;
logic [2:0] bit_idx = 0;
logic [7:0] data_reg = 0;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE; rx_valid <= 0; rx_error <= 0;
clk_cnt <= 0; bit_idx <= 0;
end else begin
rx_valid <= 0; // 默认脉冲
case (state)
S_IDLE: begin
rx_error <= 0;
if (rx == 1'b0) begin // 检测起始位
clk_cnt <= 0;
state <= S_START;
end
end
S_START: begin
// 半位后采样确认起始位
if (clk_cnt >= (CLKS_PER_BIT/2) - 1) begin
if (rx == 1'b0) begin
clk_cnt <= 0; bit_idx <= 0;
state <= S_DATA;
end else state <= S_IDLE; // 假起始位
end else clk_cnt <= clk_cnt + 1;
end
S_DATA: begin
if (clk_cnt >= CLKS_PER_BIT - 1) begin
clk_cnt <= 0;
data_reg[bit_idx] <= rx; // 中点采样
if (bit_idx >= 7) state <= S_STOP;
else bit_idx <= bit_idx + 1;
end else clk_cnt <= clk_cnt + 1;
end
S_STOP: begin
if (clk_cnt >= CLKS_PER_BIT - 1) begin
rx_valid <= 1'b1;
rx_error <= ~rx; // 停止位应为高
state <= S_IDLE;
end else clk_cnt <= clk_cnt + 1;
end
endcase
end
end
assign rx_data = data_reg;
endmodule
顶层集成带 FIFO
// uart_top.sv
module uart_top (
input logic clk, rst_n,
input logic rx,
output logic tx,
output logic [7:0] led // 显示最近收到的字节
);
logic [7:0] rx_data, tx_data;
logic rx_valid, tx_valid, tx_ready;
uart_rx #(.CLKS_PER_BIT(434)) u_rx (
.clk, .rst_n, .rx, .rx_data, .rx_valid, .rx_error()
);
uart_tx #(.CLKS_PER_BIT(434)) u_tx (
.clk, .rst_n, .tx_data(rx_data), // 回环
.tx_valid(rx_valid),
.tx_ready(tx_ready), .tx
);
// 显示最近收到的字节
always_ff @(posedge clk) begin
if (rx_valid) led <= rx_data;
end
endmodule
仿真验证
iverilog -g2012 -o uart_tb uart_tx.sv uart_rx.sv tb_uart.sv
vvp uart_tb
gtkwave uart.vcd
关键点
- 过采样:接收器在每位中点采样(起始位半位后开始),提高抗噪能力
- 假起始位过滤:检测到下降沿后半位再确认
- tx_ready 握手:发送前检查 ready,避免覆盖正在发送的数据
- 115200 @ 50MHz = 434 时钟/位,实际偏差 < 0.02%,可靠通信