跳到主要内容
返回模板库

UART 收发器UART Transceiver (TX + RX)

基于 VLSI-Shubh/UART 的 UART 收发器,含 16x 过采样 TX/RX 和波特率发生器。

UARTmasterverilog
开源标注
许可证: MIT
Author: Shubham Kapil Upadhyay
Copyright (c) 2025 Shubham Kapil Upadhyay

Key Points

  • - TX: start bit + 8 data bits (LSB first) + stop bit
  • - RX: 16x oversampling with mid-bit sampling
  • - Baud generator: configurable rate via parameters
  • - Full-duplex operation (independent TX and RX)

RTL Source

// Copyright (c) 2025 Shubham Kapil Upadhyay
// SPDX-License-Identifier: MIT
// Source: https://github.com/VLSI-Shubh/UART
// Annotated UART TX/RX with baud generator
// Based on uart_tx, uart_rx, baudgen modules from VLSI-Shubh/UART
// 中文:UART 收发器模板,基于 VLSI-Shubh/UART 项目

`timescale 1ns / 1ps

// UART Transmitter with 16x oversampling
// 中文:UART 发送器,16 倍过采样,帧格式:起始位+8数据位+停止位
// Frame: 1 start bit + 8 data bits (LSB first) + 1 stop bit
module uart_tx (
    input  wire [7:0]  data_in,     // Parallel data input
    input  wire        clk,         // System clock
    input  wire        rst,         // Synchronous reset
    input  wire        start_tx,    // Start transmission
    input  wire        baud_tick,   // Baud rate tick (16x oversampling)
    output reg         tx_done,     // Transmission complete flag
    output reg         tx_line      // UART TX serial output
);

    // FSM state encoding (one-hot)
    parameter [3:0] S_IDLE  = 4'b0001,  // Idle: TX line high
                    S_START = 4'b0010,  // Send start bit (low)
                    S_DATA  = 4'b0100,  // Send 8 data bits
                    S_STOP  = 4'b1000;  // Send stop bit (high)

    reg [3:0]  ps, ns;                  // Present state, next state
    reg [3:0]  tick_counter;            // 16x oversampling counter
    reg [2:0]  bit_index;               // Current data bit index
    reg [7:0]  tx_data;                 // Latched TX data

    // Sequential: state register
    always @(posedge clk) begin
        if (rst) begin
            ps           <= S_IDLE;
            tick_counter <= 0;
            bit_index    <= 0;
        end else begin
            ps <= ns;
        end
    end

    // Combinational: next-state logic
    always @(*) begin
        case (ps)
            S_IDLE: begin
                if (start_tx)
                    ns = S_START;
                else
                    ns = S_IDLE;
            end
            S_START: begin
                if (baud_tick)
                    ns = S_DATA;
                else
                    ns = S_START;
            end
            S_DATA: begin
                if (baud_tick && bit_index == 7)
                    ns = S_STOP;
                else
                    ns = S_DATA;
            end
            S_STOP: begin
                if (baud_tick && tx_done)
                    ns = S_IDLE;
                else
                    ns = S_STOP;
            end
            default: ns = ps;
        endcase
    end

    // Output logic
    always @(posedge clk) begin
        case (ps)
            S_IDLE: begin
                tx_line <= 1;            // Idle high
                if (start_tx) begin
                    tx_done <= 0;
                    tx_data <= data_in;  // Latch input data
                end else begin
                    tx_done <= 0;
                end
            end
            S_START: begin
                tx_done     <= 0;
                tx_line     <= 0;        // Start bit = 0
                bit_index   <= 0;
            end
            S_DATA: begin
                if (baud_tick) begin
                    if (tick_counter == 0) begin
                        tx_line <= tx_data[bit_index];  // Output data bit
                    end
                    if (tick_counter == 15) begin
                        bit_index <= bit_index + 1;
                    end
                end
            end
            S_STOP: begin
                if (baud_tick) begin
                    tx_line <= 1;        // Stop bit = 1
                    if (tick_counter == 15) begin
                        tx_done <= 1;    // Signal completion
                    end
                end
            end
            default: begin
                tx_done <= 1;
                tx_line <= 1;
            end
        endcase
    end

    // 16x oversampling tick counter
    always @(posedge clk) begin
        if (rst) begin
            tick_counter <= 0;
        end else if (ps == S_IDLE) begin
            tick_counter <= 0;
        end else if (baud_tick) begin
            if (tick_counter == 15)
                tick_counter <= 0;
            else
                tick_counter <= tick_counter + 1;
        end
    end

endmodule


// UART Receiver with 16x oversampling
// Detects start bit, samples data at mid-bit, outputs parallel data
module uart_rx (
    input  wire        rx,           // UART RX serial input
    input  wire        clk,          // System clock
    input  wire        rst,          // Synchronous reset
    input  wire        baud_tick,    // Baud rate tick (16x oversampling)
    output reg         rx_done,      // Data received flag
    output reg  [7:0]  data_out      // Received parallel data
);

    // FSM state encoding (one-hot)
    parameter [3:0] S_IDLE  = 4'b0001,
                    S_START = 4'b0010,
                    S_DATA  = 4'b0100,
                    S_STOP  = 4'b1000;

    reg [3:0]  ps, ns;
    reg [3:0]  tick_counter;
    reg [3:0]  bit_index;
    reg [7:0]  rx_data;

    // Sequential: state register
    always @(posedge clk) begin
        if (rst) begin
            ps           <= S_IDLE;
            tick_counter <= 0;
            bit_index    <= 0;
        end else begin
            ps <= ns;
        end
    end

    // Combinational: next-state logic
    always @(*) begin
        case (ps)
            S_IDLE: begin
                if (~rx)                // Detect start bit (falling edge)
                    ns = S_START;
                else
                    ns = S_IDLE;
            end
            S_START: begin
                if (baud_tick && tick_counter == 7)  // Sample at mid-bit
                    ns = S_DATA;
                else
                    ns = S_START;
            end
            S_DATA: begin
                if (baud_tick && bit_index > 7)
                    ns = S_STOP;
                else
                    ns = S_DATA;
            end
            S_STOP: begin
                if (baud_tick && tick_counter == 7)
                    ns = S_IDLE;
                else
                    ns = S_STOP;
            end
            default: ns = S_IDLE;
        endcase
    end

    // Output logic and data sampling
    always @(posedge clk) begin
        if (rst) begin
            tick_counter <= 0;
            bit_index    <= 0;
            rx_done      <= 0;
            rx_data      <= 0;
            data_out     <= 0;
        end else begin
            case (ps)
                S_IDLE: begin
                    if (baud_tick) begin
                        rx_done      <= 0;
                        tick_counter <= 0;
                        bit_index    <= 0;
                    end
                end
                S_START: begin
                    rx_done <= 0;
                    if (baud_tick) begin
                        if (tick_counter == 7)
                            tick_counter <= 0;
                        else
                            tick_counter <= tick_counter + 1;
                    end
                end
                S_DATA: begin
                    if (baud_tick) begin
                        if (bit_index < 8) begin
                            if (tick_counter == 7) begin
                                rx_data[bit_index] <= rx;  // Sample at mid-bit
                            end
                            if (tick_counter == 15) begin
                                bit_index    <= bit_index + 1;
                                tick_counter <= 0;
                            end else begin
                                tick_counter <= tick_counter + 1;
                            end
                        end
                    end
                end
                S_STOP: begin
                    rx_done <= 0;
                    if (baud_tick) begin
                        if (tick_counter == 7) begin
                            data_out     <= rx_data;
                            rx_done      <= 1;
                            tick_counter <= 0;
                        end else begin
                            tick_counter <= tick_counter + 1;
                        end
                    end
                end
                default: begin
                    rx_done      <= 0;
                    bit_index    <= 0;
                    tick_counter <= 0;
                end
            endcase
        end
    end

endmodule


// Baud Rate Generator with 16x oversampling
// Generates baud_tick pulses at 16x the desired baud rate
module baudgen #(
    parameter BAUD_RATE  = 9600,        // Target baud rate
    parameter CLOCK_FREQ = 50_000_000   // System clock frequency
)(
    input  wire clk,
    input  wire rst,
    output reg  baud_tick
);

    // Calculate divider value for 16x oversampling
    localparam TICKS = CLOCK_FREQ / (BAUD_RATE * 16);

    reg [$clog2(TICKS)-1:0] baud_counter;

    always @(posedge clk) begin
        if (rst) begin
            baud_counter <= 0;
            baud_tick    <= 0;
        end else begin
            if (baud_counter == TICKS - 1) begin
                baud_counter <= 0;
                baud_tick    <= 1;
            end else begin
                baud_counter <= baud_counter + 1;
                baud_tick    <= 0;
            end
        end
    end

endmodule