UART Controller in Verilog

UART Transmitter


module uart_tx (
    input wire clk,               // System clock
    input wire reset,             // Reset signal
    input wire [7:0] data,        // Data to transmit
    input wire start_tx,          // Start transmission signal
    output reg tx,                // UART transmit line
    output reg busy,              // Transmission busy flag
    output reg done               // Transmission done flag
);
    parameter CLK_DIV = 16;       // Clock division for baud rate
    reg [3:0] state;              // State machine state
    reg [3:0] bit_cnt;            // Bit counter
    reg [7:0] shift_reg;          // Shift register for data
    reg [7:0] clk_div_cnt;        // Clock division counter

    parameter IDLE = 4'b0000,
              START = 4'b0001,
              SEND_DATA = 4'b0010,
              STOP = 4'b0011;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            state <= IDLE;
            tx <= 1'b1;          // Idle state is high
            busy <= 1'b0;
            done <= 1'b0;
            bit_cnt <= 4'b0000;
            clk_div_cnt <= 8'b0;
        end else begin
            clk_div_cnt <= clk_div_cnt + 1;

            if (clk_div_cnt == CLK_DIV - 1) begin
                clk_div_cnt <= 8'b0; // Reset clock division counter

                case (state)
                    IDLE: begin
                        done <= 1'b0;
                        if (start_tx) begin
                            busy <= 1'b1;
                            state <= START;
                            shift_reg <= data; // Load data to shift register
                            bit_cnt <= 4'b0000; // Reset bit counter
                            tx <= 1'b0; // Start bit
                        end
                    end

                    START: begin
                        state <= SEND_DATA;
                    end

                    SEND_DATA: begin
                        if (bit_cnt < 8) begin
                            tx <= shift_reg[0]; // Send LSB first
                            shift_reg <= {1'b0, shift_reg[7:1]}; // Shift data
                            bit_cnt <= bit_cnt + 1;
                        end else begin
                            state <= STOP; // Move to stop bit
                        end
                    end

                    STOP: begin
                        tx <= 1'b1; // Stop bit
                        busy <= 1'b0;
                        done <= 1'b1; // Transmission done
                        state <= IDLE; // Go back to idle
                    end

                    default: state <= IDLE; // Default to idle
                endcase
            end
        end
    end
endmodule

UART Receiver


module uart_rx (
    input wire clk,               // System clock
    input wire reset,             // Reset signal
    input wire rx,                // UART receive line
    output reg [7:0] data,        // Received data
    output reg data_ready,        // Data ready signal
    output reg busy               // Receiver busy flag
);
    parameter CLK_DIV = 16;       // Clock division for baud rate
    reg [3:0] state;              // State machine state
    reg [3:0] bit_cnt;            // Bit counter
    reg [7:0] shift_reg;          // Shift register for data
    reg [7:0] clk_div_cnt;        // Clock division counter

    parameter IDLE = 4'b0000,
              START = 4'b0001,
              RECEIVE_DATA = 4'b0010,
              STOP = 4'b0011;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            state <= IDLE;
            data_ready <= 1'b0;
            busy <= 1'b0;
            bit_cnt <= 4'b0000;
            clk_div_cnt <= 8'b0;
        end else begin
            clk_div_cnt <= clk_div_cnt + 1;

            if (clk_div_cnt == CLK_DIV - 1) begin
                clk_div_cnt <= 8'b0; // Reset clock division counter

                case (state)
                    IDLE: begin
                        data_ready <= 1'b0;
                        if (rx == 1'b0) begin // Start bit detected
                            busy <= 1'b1;
                            state <= START;
                        end
                    end

                    START: begin
                        state <= RECEIVE_DATA;
                        bit_cnt <= 4'b0000; // Reset bit counter
                    end

                    RECEIVE_DATA: begin
                        if (bit_cnt < 8) begin
                            shift_reg[bit_cnt] <= rx; // Receive data
                            bit_cnt <= bit_cnt + 1;
                        end else begin
                            state <= STOP; // Move to stop bit
                        end
                    end

                    STOP: begin
                        data <= shift_reg; // Store received data
                        data_ready <= 1'b1; // Indicate data is ready
                        busy <= 1'b0;
                        state <= IDLE; // Go back to idle
                    end

                    default: state <= IDLE; // Default to idle
                endcase
            end
        end
    end
endmodule