Communication
UART transmitter
uart_tx
Serial transmitter with 8 data bits, no parity, and 1 stop bit, least significant bit first. Pulse start for one cycle while busy is low to send data; done pulses after the stop bit. Pair it with uart_rx for a full UART, or connect tx to a board's USB-serial pin.
Instantiate it
Paste into your design and connect the signals.
uart_tx #(.CLKS_PER_BIT(868)) u_uart_tx (
.clk(clk), .rst(rst),
.start(send), .data(byte_to_send),
.tx(uart_tx_pin), .busy(tx_busy), .done(tx_done)
);Parameters
| Name | Default | Meaning |
|---|---|---|
| CLKS_PER_BIT | 868 | Clock cycles per bit: clock frequency divided by baud rate (100 MHz / 115200 = 868). |
Ports
| Name | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | Clock; everything changes on its rising edge. |
| rst | input | 1 | Synchronous reset, active high. |
| start | input | 1 | Pulse for one cycle to send data (ignored while busy). |
| data | input | 8 | The byte to send, read when start is high. |
| tx | output | 1 | Serial line; idles high. |
| busy | output | 1 | High while a byte is being sent. |
| done | output | 1 | Pulses for one cycle after the stop bit. |
Add it to a project
It is copied to ip/uart_tx/uart_tx.sv with a README (ip/uart_tx/README.md). When your design needs it, the Architect (new designs) or the Architect (changes) suggests it and asks before using it. Or add it yourself:
- In the workspace: the Tapeo Hub button in the project header, then Add to project.
- In a terminal (in the project folder):
$ tapeo hub add uart_tx - In VS Code: Tapeo: Add from Tapeo Hub in the Command Palette.
How it is verified
Before every release Tapeo checks this block: its self-checking testbench passes in Icarus Verilog, Verilator -Wall reports nothing, and Yosys synthesizes it (124 generic cells at the default parameters). That shows it works for the tested cases; your copy is part of your project, and your own testbench checks it in your design.
Source
// UART transmitter, 8 data bits, no parity, 1 stop bit (8N1), LSB first.
// Pulse `start` for one cycle while `busy` is low to send `data`; `done`
// pulses for one cycle after the stop bit. Tapeo Hub (verified by Tapeo).
module uart_tx #(
parameter integer CLKS_PER_BIT = 868 // clock frequency / baud rate, for example 100 MHz / 115200
) (
input logic clk,
input logic rst,
input logic start,
input logic [7:0] data,
output logic tx,
output logic busy,
output logic done
);
localparam integer COUNT_WIDTH = (CLKS_PER_BIT > 1) ? $clog2(CLKS_PER_BIT) : 1;
localparam logic [COUNT_WIDTH-1:0] LAST_TICK = COUNT_WIDTH'(CLKS_PER_BIT - 1);
typedef enum logic [1:0] { IDLE, START, DATA, STOP } state_t;
state_t state;
logic [COUNT_WIDTH-1:0] ticks;
logic [2:0] bit_index;
logic [7:0] shift;
always_ff @(posedge clk) begin
if (rst) begin
state <= IDLE;
ticks <= '0;
bit_index <= '0;
shift <= '0;
tx <= 1'b1;
done <= 1'b0;
end else begin
done <= 1'b0;
case (state)
IDLE: begin
tx <= 1'b1;
if (start) begin
shift <= data;
ticks <= '0;
tx <= 1'b0;
state <= START;
end
end
START: begin
if (ticks == LAST_TICK) begin
ticks <= '0;
bit_index <= '0;
tx <= shift[0];
state <= DATA;
end else ticks <= ticks + 1'b1;
end
DATA: begin
if (ticks == LAST_TICK) begin
ticks <= '0;
if (bit_index == 3'd7) begin
tx <= 1'b1;
state <= STOP;
end else begin
bit_index <= bit_index + 1'b1;
tx <= shift[bit_index + 1'b1];
end
end else ticks <= ticks + 1'b1;
end
STOP: begin
if (ticks == LAST_TICK) begin
ticks <= '0;
done <= 1'b1;
state <= IDLE;
end else ticks <= ticks + 1'b1;
end
default: state <= IDLE;
endcase
end
end
assign busy = (state != IDLE);
endmodule