Files
2026-06-10 09:32:26 +02:00

82 lines
2.9 KiB
Verilog

// uart_tx.v
//
// Byte-at-a-time UART transmitter. 8N1, no flow control.
// Default 115200 baud from a 50 MHz clock (divisor = 434, ~0.06% error).
//
// Handshake: assert `start` for one clk with `data` valid. The byte is
// captured on that edge; `busy` then goes high until the stop bit
// completes. Hold off further `start` pulses while `busy` is high.
module uart_tx #(
parameter integer CLK_HZ = 50_000_000,
parameter integer BAUD = 115_200
)(
input wire clk,
input wire rst_n,
input wire start,
input wire [7:0] data,
output reg tx,
output reg busy
);
localparam integer DIV = (CLK_HZ + BAUD/2) / BAUD;
localparam integer DW = $clog2(DIV);
localparam [3:0] S_IDLE = 4'd0,
S_START = 4'd1,
S_D0 = 4'd2, S_D1 = 4'd3, S_D2 = 4'd4, S_D3 = 4'd5,
S_D4 = 4'd6, S_D5 = 4'd7, S_D6 = 4'd8, S_D7 = 4'd9,
S_STOP = 4'd10;
reg [3:0] state;
reg [DW-1:0] tick;
reg [7:0] shift;
wire tick_done = (tick == DIV-1);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE;
tick <= 0;
shift <= 8'd0;
tx <= 1'b1;
busy <= 1'b0;
end else begin
case (state)
S_IDLE: begin
tx <= 1'b1;
busy <= 1'b0;
tick <= 0;
if (start) begin
shift <= data;
state <= S_START;
busy <= 1'b1;
tx <= 1'b0; // start bit asserted immediately
end
end
default: begin
if (tick_done) begin
tick <= 0;
case (state)
S_START: begin tx <= shift[0]; state <= S_D0; end
S_D0: begin tx <= shift[1]; state <= S_D1; end
S_D1: begin tx <= shift[2]; state <= S_D2; end
S_D2: begin tx <= shift[3]; state <= S_D3; end
S_D3: begin tx <= shift[4]; state <= S_D4; end
S_D4: begin tx <= shift[5]; state <= S_D5; end
S_D5: begin tx <= shift[6]; state <= S_D6; end
S_D6: begin tx <= shift[7]; state <= S_D7; end
S_D7: begin tx <= 1'b1; state <= S_STOP; end
S_STOP: begin state <= S_IDLE; busy <= 1'b0; end
default: state <= S_IDLE;
endcase
end else begin
tick <= tick + 1'b1;
end
end
endcase
end
end
endmodule