进阶:UART 收发器(可综合到 FPGA)

知识库
知识库文档
/tech-stacks/verilog-systemverilog/examples/进阶:UART 收发器(可综合到 FPGA).md

文档

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%,可靠通信

信息

路径
/tech-stacks/verilog-systemverilog/examples/进阶:UART 收发器(可综合到 FPGA).md
更新时间
2026/5/31