Project 15: UART Transmitter

Transmit bytes using UART framing with a baud generator.

Quick Reference

Attribute Value
Difficulty Advanced
Time Estimate 1 week
Main Programming Language Verilog (Alternatives: VHDL, SystemVerilog)
Alternative Programming Languages VHDL, SystemVerilog
Coolness Level High
Business Potential Medium
Prerequisites Shift registers, Counters, Serial protocols
Key Topics UART framing, Baud generator

1. Learning Objectives

  1. Generate UART frames in hardware
  2. Compute baud divisors from clock
  3. Expose busy/ready signals

2. All Theory Needed (Per-Concept Breakdown)

UART Framing and Baud Generation

Description/Expanded Explanation of the concept

UART is asynchronous serial communication. Each byte is framed with a start bit, data bits, optional parity, and stop bit. A baud generator creates a tick to advance the TX or RX state.

Definitions & Key Terms
  • Baud -> symbols per second
  • Start bit -> logic 0 indicating frame start
  • Stop bit -> logic 1 indicating frame end
Mental Model Diagram (ASCII)
idle(1) start(0) d0 d1 d2 d3 d4 d5 d6 d7 stop(1)

UART frame bits

How It Works (Step-by-Step)
  1. Compute divisor from clock to target baud.
  2. Generate a baud tick.
  3. Shift out bits at each tick.
Minimal Concrete Example
if (baud_tick) begin
  tx <= shift[0];
  shift <= {1'b1, shift[9:1]};
end
Common Misconceptions
  • “UART is clocked.” -> It is asynchronous.
  • “Baud doesn’t need to be exact.” -> Too much error breaks framing.
Check-Your-Understanding Questions
  1. What does 8N1 mean?
  2. How do you compute a baud divider?
  3. Why is idle high?
Where You’ll Apply It
  • This project: used in Section 3.2 and Section 5
  • Also used in: P16-uart-receiver.md

Shift Registers

Description/Expanded Explanation of the concept

Shift registers move data one bit per clock. They are the core of serial protocols and LED chasers. They can shift left, right, or rotate, and they are often paired with counters for timing.

Definitions & Key Terms
  • Shift register -> register that shifts bits each cycle
  • Serial -> one bit per clock
  • Parallel -> multiple bits at once
Mental Model Diagram (ASCII)
[ b7 b6 b5 b4 b3 b2 b1 b0 ] -> shift -> [ b6 ... b0 new ]

Shift register bit movement

How It Works (Step-by-Step)
  1. Load a register with a value.
  2. On each clock edge, shift left or right.
  3. Insert a new bit at the shifted end.
Minimal Concrete Example
shift <= {shift[6:0], serial_in};
Common Misconceptions
  • “Shift registers are only for serial I/O.” -> They are also for patterns and delays.
  • “Shifts are free.” -> They still consume registers and routing.
Check-Your-Understanding Questions
  1. What is the difference between shift and rotate?
  2. How many cycles to shift out 8 bits?
  3. Why use shift registers in UART TX?
Where You’ll Apply It
  • This project: used in Section 3.2 and Section 4
  • Also used in: P15-uart-transmitter.md, P17-spi-master.md

Verification with Testbenches and Waveforms

Description/Expanded Explanation of the concept

Testbenches are simulation-only modules that apply stimulus and check outputs. Waveforms (VCD) are the hardware engineer’s microscope; they reveal timing, glitches, and ordering problems. A good testbench is deterministic and covers edge cases.

Definitions & Key Terms
  • Testbench -> a non-synthesizable module that drives a DUT
  • VCD -> Value Change Dump waveform file
  • Deterministic test -> same inputs produce same outputs every run
Mental Model Diagram (ASCII)
[Testbench] -> [DUT] -> [VCD] -> [GTKWave]

Testbench DUT VCD GTKWave flow

How It Works (Step-by-Step)
  1. Initialize inputs to known values.
  2. Apply stimulus over time.
  3. Dump waveforms and check outputs.
  4. Add assertions or PASS/FAIL messages.
Minimal Concrete Example
initial begin
  $dumpfile("wave.vcd");
  $dumpvars(0, tb);
  a = 0; b = 1; #10;
  $finish;
end
Common Misconceptions
  • “If it simulates once, it’s correct.” -> Cover all relevant cases.
  • “Waveforms are optional.” -> They are often the only way to debug timing.
Check-Your-Understanding Questions
  1. Why keep testbench and DUT separate?
  2. What is the purpose of $dumpvars?
  3. How do you make a testbench deterministic?
Where You’ll Apply It
  • This project: used throughout Section 6 (testing)
  • Also used in: all other projects in this folder

3. Project Specification

3.1 What You Will Build

A UART TX module supporting 8N1 at configurable baud.

3.2 Functional Requirements

  1. Requirement 1: Frame bytes with start/data/stop
  2. Requirement 2: Generate baud tick from clock
  3. Requirement 3: Provide busy/ready handshaking

3.3 Non-Functional Requirements

  • Performance: Stable operation at the target clock and interfaces.
  • Reliability: Deterministic outputs on all defined inputs.
  • Usability: Clear ports and documented behavior.

3.4 Example Usage / Output

{p['example_usage']}

3.5 Data Formats / Schemas / Protocols

{p[‘data_format’]}

3.6 Edge Cases

  • Back-to-back bytes
  • Baud mismatch

3.7 Real World Outcome

3.7.1 How to Run (Copy/Paste)

vvp uart_tx_tb

3.7.2 Golden Path Demo (Deterministic)

Run the demo command above with the provided testbench and confirm the outputs match the golden transcript.

3.7.3 CLI Transcript

TX byte=0x55
frame: start 01010101 stop

3.7.4 Failure Demo (Expected)

# Example failure case
ERROR: Output mismatch at vector 3
Expected: 0x0A, Got: 0x0B
EXIT CODE: 1

Notes:

  • Exit code 0 indicates all tests passed
  • Exit code 1 indicates a test failure

4. Solution Architecture

4.1 High-Level Design

[inputs] -> [core logic] -> [outputs]

Core logic flow

4.2 Key Components

Component Responsibility
baud_gen Baud tick generator
tx_fsm Frame sequencing

4.3 Data Structures (No Full Code)

// Example signals (adapt to your design)
reg [7:0] state_reg;
reg [7:0] data_reg;

4.4 Algorithm Overview

Key Algorithm: Core control flow

  1. Initialize state/reset conditions.
  2. Apply inputs and compute outputs.
  3. Update state on clock edges (if sequential).

Complexity Analysis:

  • Time: O(1) per cycle
  • Space: O(N) for registers and logic

5. Implementation Guide

5.1 Development Environment Setup

iverilog -v
# Ensure GTKWave is installed for waveform viewing

5.2 Project Structure

project-root/
|-- src/
|   |-- top.v
|   |-- core.v
|-- tb/
|   |-- tb.v
|-- Makefile
|-- README.md

Project folder structure

5.3 The Core Question You’re Answering

“How do you serialize data into a real-world asynchronous protocol?”

5.4 Concepts You Must Understand First

  • Shift registers
  • Counters
  • Serial protocols

5.5 Questions to Guide Your Design

  • What baud rate and clock are you using?
  • How do you signal ready for next byte?

5.6 Thinking Exercise

Compute the divider for 115200 baud at 50 MHz.

5.7 The Interview Questions They’ll Ask

  • What does 8N1 mean?
  • Why is the line idle high?

5.8 Hints in Layers

  • Load a shift register with start/data/stop.
  • Shift on each baud tick.

5.9 Books That Will Help

Topic Book Chapter
Serial protocols Making Embedded Systems Ch. 8

5.10 Implementation Phases

Phase 1: Foundation

Goals:

  • Establish core module structure
  • Implement minimal behavior

Tasks:

  1. Scaffold module ports and internal signals
  2. Write a minimal testbench that compiles

Checkpoint: Simulation runs without errors

Phase 2: Core Functionality

Goals:

  • Implement full logic
  • Verify edge cases

Tasks:

  1. Complete core logic
  2. Add directed tests for edge cases

Checkpoint: All tests pass and waveforms match expectations

Phase 3: Polish & Edge Cases

Goals:

  • Improve readability
  • Document behavior

Tasks:

  1. Add comments and README notes
  2. Expand tests for unusual inputs

Checkpoint: Design is deterministic and documented

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Reset strategy Sync / Async Sync Simpler timing closure
Test coverage Directed / Exhaustive Exhaustive for small logic Prevents missed cases

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Unit Tests Test core logic Small vectors
Integration Tests Test modules together Full system
Edge Case Tests Boundary conditions Max/min values

6.2 Critical Test Cases

  1. Test 1: Check bit width vs baud
  2. Test 2: Back-to-back frame testing

6.3 Test Data

Use deterministic vectors and document expected outputs.

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Wrong baud Frame too fast/slow Fix divider calculation

7.2 Debugging Strategies

  • Inspect waveforms at key internal signals
  • Add temporary debug outputs to verify state
  • Reduce testcases to the smallest failing case

7.3 Performance Traps

  • Overly wide counters or combinational paths can reduce max clock

8. Extensions & Challenges

8.1 Beginner Extensions

  • Add parameterization for widths
  • Add optional features (enable, reset)

8.2 Intermediate Extensions

  • Add configuration registers
  • Build a simple driver or demo program

8.3 Advanced Extensions

  • Integrate with another project in this series
  • Implement a hardware demo on FPGA

9. Real-World Connections

9.1 Industry Applications

  • Digital control systems and embedded peripherals
  • FPGA prototyping and validation
  • Yosys / nextpnr toolchain for open-source FPGA flow
  • Example HDL projects in the FPGA community

9.3 Interview Relevance

  • Demonstrates RTL thinking and verification skills

10. Resources

10.1 Essential Reading

  • Making Embedded Systems - Focus on Ch. 8

10.2 Video Resources

  • Search for project-specific HDL walkthroughs and waveforms

10.3 Tools & Documentation

  • Icarus Verilog
  • GTKWave
  • See adjacent projects in VERILOG_FROM_ZERO_PROJECTS/

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain the core concept without notes
  • I can predict waveform behavior for basic inputs

11.2 Implementation

  • All functional requirements are met
  • All tests pass
  • Edge cases are documented

11.3 Growth

  • I can explain this project in an interview
  • I documented at least one lesson learned

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Functional requirements implemented
  • Testbench passes
  • Waveforms inspected

Full Completion:

  • All minimum criteria plus
  • Edge cases covered and documented

Excellence (Going Above & Beyond):

  • Hardware demo on FPGA
  • Clear write-up of lessons learned

    Appendix A: Deep Dive Walkthrough

A.1 Signal Map and Timing Contract

  • Inputs: clk, reset, tx_start, tx_data[7:0]
  • Outputs: tx, tx_busy

Invariant: When tx_start is accepted, tx_busy stays high for exactly 10 bit periods (start + 8 data + stop).

A.2 Baud Divider Example (50 MHz -> 115200)

  • Divider = 50,000,000 / 115,200 = 434.027…
  • Use 434 for a small positive error (0.006%).

A.3 Frame Timeline

Idle(1) | Start(0) | d0 d1 d2 d3 d4 d5 d6 d7 | Stop(1)

UART frame bits

A.4 Deterministic Test Vectors

  • Send 0x55 -> expect 01010101 LSB-first.
  • Send 0xA5 -> expect 10100101 LSB-first.

A.5 Debugging Tip

If the receiver sees framing errors, verify the stop bit is high and lasts exactly one bit period.

13. Deep Dive Appendix

13.1 Timing and Resource Budget

  • Baud divisor = clk_hz / baud. Example: 50 MHz / 115200 = 434.027.
  • Choose integer divisor 434 and accept about 0.006% error.
  • Total frame time for 8N1 is 10 bit times.

13.2 Waveform Interpretation Guide

  • tx should idle high, then go low for start, then LSB-first data, then stop high.
  • Verify busy remains high for exactly 10 baud ticks per byte.

Example:

byte=0x55 -> 0 1 0 1 0 1 0 1 0 1 (start, data, stop)

13.3 Hardware Bring-Up Notes

  • Connect TX to a USB-serial adapter (3.3V logic).
  • Use a terminal at the matching baud to verify output.
  • If output looks garbled, check baud divider and bit order.

13.4 Alternate Implementations and Trade-offs

  • Fractional baud generator for improved accuracy.
  • Add parity or 9-bit support for protocols like MODBUS.
  • Add a TX FIFO to decouple producer and transmitter.

13.5 Additional Exercises

  • Add a break condition (hold TX low for more than 1 frame).
  • Implement a command frame with checksum.
  • Add flow control (CTS/RTS) as optional signals.