82 lines
2.9 KiB
Verilog
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
|