# UART TX (bit-bang)

## What You Will Learn

UART transmits serial frames: start bit, data LSB-first, stop bit. `uart_tx` FSM walks `IDLE` → `START` → `DATA` → `STOP` with a bit index and shift register—sample `tx` on the waveform at one clock per baud for clarity.

## Learning Objectives

- Map FSM states to line levels on `tx`.
- Follow `shreg` shifts LSB-first onto `tx` in `DATA`.
- Use `busy` to know when a new `start` is legal.
- Decode 8N1 framing on the serial line in waves.

## Theory

Idle high (`tx=1`); start bit pulls low; eight data bits MSB/LSB order here LSB via `shreg[0]`; stop returns high. `bit_idx` counts 0..7 in DATA. Real UARTs add baud dividers—this example uses one clock per bit so waves are readable.

Serial protocols trade pin count for time—ubiquitous for debug consoles and GPS modules.

## When to use this pattern

Bring-up debug, GPS, Bluetooth module AT commands, and legacy sensor links still use UART—know the bit-serial pattern.

## Files

| File | Role |
|------|------|
| `tb/tb_uart.v` | Testbench — **`tb_uart`** |
| `rtl/uart_tx.v` | RTL / DUT — **`uart_tx`** |

## Practice Architecture

```mermaid
flowchart TB
  tb[tb_uart] -->|clk rst_n start data| dut[uart_tx]
  dut -->|tx busy| tb
```

## What to explore

- Trigger `start` with `data=8'hA5` and sketch expected `tx` bit sequence.
- Verify `busy` high for entire frame duration.
- Count clocks per character frame (start + 8 data + stop).
- Compare FSM style to `packet_tx_fsm`—simpler outputs, serial datapath.
- Discuss baud divider insertion point for real baud rates.

## Summary

UART TX makes bit-serial timing concrete—FSM plus shifter, visible one bit per clock on `tx`.

# Appendix (Code Review)

This section is an optional deep dive into the example source files. Line numbers refer to the copies in your workspace under `rtl/` and `tb/`.

## rtl/uart_tx.v

**Purpose:** Minimal 8N1 UART transmitter (`uart_tx`).

Lines 8–52 — Module `uart_tx`

```verilog
`default_nettype none
// Ultra-simple UART TX: 8N1, 1 clock per baud (use slow baud in TB for waves).
module uart_tx (
  input wire       clk,
  input wire       rst_n,
  input wire       start,
  input wire [7:0] data,
  output reg tx,
  output reg busy
);
  localparam [1:0] IDLE  = 2'd0;
  localparam [1:0] START = 2'd1;
  localparam [1:0] DATA  = 2'd2;
  localparam [1:0] STOP  = 2'd3;

  reg [1:0] st;
  reg [7:0] shreg;
  reg [2:0] bit_idx;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st <= IDLE; tx <= 1'b1; busy <= 1'b0; shreg <= 8'h00; bit_idx <= 3'd0;
    end else begin
      case (st)
        IDLE: begin
          tx <= 1'b1; busy <= 1'b0;
          if (start) begin
            shreg <= data; bit_idx <= 3'd0; busy <= 1'b1; st <= START;
          end
        end
        START: begin
          tx <= 1'b0; st <= DATA;
        end
        DATA: begin
          tx <= shreg[0];
          shreg <= {1'b0, shreg[7:1]};
          if (bit_idx == 3'd7) st <= STOP;
          else bit_idx <= bit_idx + 3'd1;
        end
        STOP: begin
          tx <= 1'b1; st <= IDLE;
        end
        default: st <= IDLE;
      endcase
    end
  end
endmodule
```

**Code review:** FSM: `IDLE`, `START`, `DATA`, `STOP`. Shifter: `shreg` holds data; LSB driven to `tx` each DATA cycle. bit_idx: Counts data bits 0..7 before STOP. busy: High during frame transmission; idle accepts new `start`. Line idles high: Start bit = 0, stop bit = 1.

## tb/tb_uart.v

**Purpose:** Testbench `tb_uart` — elaborates `uart_tx` and captures waves under `sim/`.

Lines 7–32 — Module `tb_uart`

```verilog
`timescale 1ns/1ps
module tb_uart;
  reg clk, rst_n, start, tx, busy;
  reg [7:0] data;
  uart_tx u_dut (
    .clk(clk),
    .rst_n(rst_n),
    .start(start),
    .data(data),
    .tx(tx),
    .busy(busy)
  );
  initial clk = 0;
  always #5 clk = ~clk;
  initial begin
    $dumpfile("sim/dump.vcd");
    $dumpvars(0, tb_uart);
    rst_n = 0; start = 0; data = 8'h00;
    #22 rst_n = 1;
    @(posedge clk);
    data = 8'hA5; start = 1;
    @(posedge clk); start = 0;
    repeat (16) @(posedge clk);
    $display("UART byte 0xA5 shifted on tx (see waves)");
    $finish;
  end
endmodule
```

**Code review:** `tb_uart` instantiates `uart_tx`. Look for `$dumpfile` / `$dumpvars` in an `initial` block and any loops or `$display` checks that report PASS/FAIL.

---

© 2026 RtlStudio. Licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). Commercial use and modifications by third parties are not permitted.

© 2026 RtlStudio. All rights reserved. RTL Studio is proprietary software.