Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

基於 FPGA 的 UART 設計全解析:波特率產生、發送/接收狀態機、中間值採樣與 Verilog 完整代碼

編輯:Linux 教學

前言

UART(Universal Asynchronous Receiver/Transmitter)是 FPGA 最基礎的串行通信接口。本文系統講解 UART 幀格式、波特率產生器設計、發送器/接收器狀態機邏輯、**中間值過採樣抗抖動**、Verilog 完整可綜合代碼與關鍵時序優化點。

一、UART 幀格式與基本參數

空閒態:高電平 (1)
起始位:1 bit 低電平 (0)
數據位:5-9 bit(常用 8 bit,LSB first)
校驗位:可選(奇/偶/無)
停止位:1/1.5/2 bit 高電平 (1)

典型配置:9600bps, 8N1(8 數據、無校驗、1 停止)。

二、系統架構三大模組

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Baud Gen    │────▶│ TX State    │────▶│ TxD         │
│ (Tick Gen)  │     │ Machine     │     │ (Output)    │
└─────────────┘     └─────────────┘     └─────────────┘
      │                    ▲
      ▼                    │
┌─────────────┐     ┌─────────────┐
│ RX Over-    │     │ RX State    │
│ sampling    │────▶│ Machine     │
└─────────────┘     └─────────────┘

三、波特率產生器:分頻器 + Tick 脈衝

module baud_tick_gen #(
    parameter SYS_CLK_HZ = 50_000_000,
    parameter BAUD_RATE  = 9600,
    parameter OVERSAMPLE = 8          // 過採樣倍數 8/16
)(
    input  wire clk,
    output reg  tick
);
    localparam integer DIV = SYS_CLK_HZ / (BAUD_RATE * OVERSAMPLE) - 1;
    reg [$clog2(DIV+1)-1:0] cnt;
    always @(posedge clk) begin
        if (cnt == DIV) begin cnt <= 0; tick <= 1'b1; end
        else begin cnt <= cnt + 1; tick <= 1'b0; end
    end
endmodule

關鍵點:

  • OVERSAMPLE=8 產生 8× 波特率時鐘(76.8kHz),接收端用中間值採樣(第 4 個 tick)抗抖動。
  • 計數器位寬用 $clog2 自動推導,防止位寬不足導致分頻錯誤。

四、發送器:狀態機逐位輸出

module uart_tx #(
    parameter DATA_BITS = 8
)(
    input  wire clk,
    input  wire tick,           // 來自波特率產生器
    input  wire [DATA_BITS-1:0] din,
    input  wire din_valid,      // 脈衝啟動發送
    output reg  txd,
    output reg  busy
);
    typedef enum logic [3:0] {
        S_IDLE, S_START,
        S_BIT0, S_BIT1, S_BIT2, S_BIT3,
        S_BIT4, S_BIT5, S_BIT6, S_BIT7,
        S_STOP
    } state_t;

    state_t state, nxt_state;
    reg [3:0] bit_cnt;

    always @(posedge clk) begin
        if (tick) state <= nxt_state;
    end

    always @(*) begin
        nxt_state = state;
        case (state)
            S_IDLE:  if (din_valid) nxt_state = S_START;
            S_START:                nxt_state = S_BIT0;
            S_BIT0:  nxt_state = S_BIT1;  S_BIT1: nxt_state = S_BIT2;
            S_BIT2:  nxt_state = S_BIT3;  S_BIT3: nxt_state = S_BIT4;
            S_BIT4:  nxt_state = S_BIT5;  S_BIT5: nxt_state = S_BIT6;
            S_BIT6:  nxt_state = S_BIT7;  S_BIT7: nxt_state = S_STOP;
            S_STOP:  nxt_state = S_IDLE;
        endcase
    end

    always @(posedge clk) begin
        if (tick) begin
            case (state)
                S_IDLE:   txd <= 1'b1;
                S_START:  txd <= 1'b0;
                S_BIT0:   txd <= din[0];  S_BIT1: txd <= din[1];
                S_BIT2:   txd <= din[2];  S_BIT3: txd <= din[3];
                S_BIT4:   txd <= din[4];  S_BIT5: txd <= din[5];
                S_BIT6:   txd <= din[6];  S_BIT7: txd <= din[7];
                S_STOP:   txd <= 1'b1;
            endcase
        end
    end

    always @(posedge clk) begin
        busy <= (state != S_IDLE);
    end
endmodule

五、接收器:過採樣 + 多數表決 + 狀態機

module uart_rx #(
    parameter DATA_BITS = 8,
    parameter OVERSAMPLE = 8
)(
    input  wire clk,
    input  wire tick,           // 過採樣 tick (8×baud)
    input  wire rxd,
    output reg  [DATA_BITS-1:0] dout,
    output reg  dout_valid
);
    typedef enum logic [3:0] {
        S_IDLE, S_START,
        S_BIT0, S_BIT1, S_BIT2, S_BIT3,
        S_BIT4, S_BIT5, S_BIT6, S_BIT7,
        S_STOP
    } state_t;

    state_t state;
    reg [$clog2(OVERSAMPLE)-1:0] sample_cnt;
    reg [2:0] majority_cnt;     // 連續 0/1 計數
    reg rxd_sync1, rxd_sync2;   // 兩級同步器防亞穩態

    // 同步輸入到 clk 域
    always @(posedge clk) begin
        rxd_sync1 <= rxd;
        rxd_sync2 <= rxd_sync1;
    end

    // 過採樣多數表決:連續 3 個樣本一致才認定位值
    always @(posedge clk) begin
        if (tick) begin
            if (rxd_sync2) begin
                if (majority_cnt != 3'd7) majority_cnt <= majority_cnt + 1;
            end else begin
                if (majority_cnt != 0) majority_cnt <= majority_cnt - 1;
            end
        end
    end
    wire bit_val = (majority_cnt >= 3'd4);  // 閾值 4/8

    // 位計數器
    always @(posedge clk) begin
        if (tick) begin
            if (state == S_IDLE) sample_cnt <= 0;
            else sample_cnt <= sample_cnt + 1;
        end
    end
    wire bit_done = (sample_cnt == OVERSAMPLE-1);

    // 狀態機
    always @(posedge clk) begin
        if (tick && bit_done) begin
            case (state)
                S_IDLE:  if (!bit_val) state <= S_START;      // 檢測起始位下降沿
                S_START: state <= S_BIT0;
                S_BIT0:  state <= S_BIT1;  S_BIT1: state <= S_BIT2;
                S_BIT2:  state <= S_BIT3;  S_BIT3: state <= S_BIT4;
                S_BIT4:  state <= S_BIT5;  S_BIT5: state <= S_BIT6;
                S_BIT6:  state <= S_BIT7;  S_BIT7: state <= S_STOP;
                S_STOP:  state <= S_IDLE;
            endcase
        end
    end

    // 數據移位寄存器
    always @(posedge clk) begin
        if (tick && bit_done && state >= S_BIT0 && state <= S_BIT7) begin
            dout <= {bit_val, dout[DATA_BITS-1:1]};  // LSB first 右移
        end
    end

    // 輸出有效脈衝
    always @(posedge clk) begin
        dout_valid <= (tick && bit_done && state == S_STOP);
    end
endmodule

六、關鍵設計優化點總結

優化點說明
過採樣 8×/16×中間值採樣(第 4/8 個 tick)大幅提高抗抖動能力
多數表決濾波連續 N 個樣本一致才翻轉,濾除毛刺
兩級同步器異步 RxD 輸入同步到 clk 域,消除亞穩態風險
參數化設計波特率、數據位、過採樣倍數皆可參數化,靈活復用
Tick 脈衝而非分頻時鐘避免跨時鐘域,單時鐘域設計,時序收斂簡單
狀態機單熱編碼綜合器自動優化,面積小、時序清晰

七、例化頂層模組

module uart_top #(
    parameter SYS_CLK_HZ = 50_000_000,
    parameter BAUD_RATE  = 9600
)(
    input  wire clk,
    input  wire rst_n,
    input  wire rxd,
    output wire txd,
    // 用戶側接口
    input  wire [7:0] tx_data,
    input  wire       tx_valid,
    output wire       tx_busy,
    output wire [7:0] rx_data,
    output wire       rx_valid
);
    wire tick;
    baud_tick_gen #(.SYS_CLK_HZ(SYS_CLK_HZ), .BAUD_RATE(BAUD_RATE), .OVERSAMPLE(8))
        u_baud (.clk(clk), .tick(tick));

    uart_tx #(.DATA_BITS(8)) u_tx (
        .clk(clk), .tick(tick), .din(tx_data), .din_valid(tx_valid),
        .txd(txd), .busy(tx_busy)
    );

    uart_rx #(.DATA_BITS(8), .OVERSAMPLE(8)) u_rx (
        .clk(clk), .tick(tick), .rxd(rxd),
        .dout(rx_data), .dout_valid(rx_valid)
    );
endmodule

總結

UART 設計核心在於波特率 Tick 產生、發送器定態輸出、接收器過採樣多數表決同步。採用單時鐘域 + Tick 脈衝使能架構,配合參數化、同步器、濾波器,可獲得面積小、時序收斂易、抗干擾強的可復用 IP 核。進階可擴展:FIFO 緩衝、DMA 接口、自動波特率檢測、RS485 方向控制、LIN 協議等。

Copyright © Windows教程網 All Rights Reserved