Day 11 · UART Transmitter

Connecting to a PC

Video 4 of 4 · ~8 minutes

Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF

ProtocolTX ArchitectureImplementationPC Connection

🌍 Where This Lives

In Industry

Every FPGA dev board has a USB-UART bridge chip (FT232H, CP2102, CH340G) that translates USB to 3.3V UART. The Go Board uses an FT2232H. Your laptop sees it as /dev/ttyUSB0 (Linux), /dev/cu.usbserial-XYZ (macOS), or COM3 (Windows). Python's pyserial, screen, minicom, and PuTTY all speak to it. This standard pipeline is how every FPGA/MCU developer moves data between chip and laptop.

In This Course

Today's setup is your lifeline. Your capstone will print debug info over UART, read configuration over UART, or stream sensor data over UART. Day 12 RX lets you receive bytes FROM the PC. By the end of the week, you have full bidirectional communication.

⚠️ Voltage Levels and Polarity Matter

❌ Wrong Model

“UART is UART. Connect my FPGA's TX to any UART RX and it works.”

✓ Right Model

Modern 3.3V UART (FPGA/MCU) uses positive logic: idle=3.3V (HIGH), start=0V (LOW). True RS-232 (9-pin connectors from the PC era) uses inverted bipolar levels: idle=-12V, active=+12V. Connecting 3.3V UART directly to an RS-232 port can damage the FPGA. Go Board's on-board USB-UART bridge handles conversion to 3.3V; external RS-232 devices need a MAX3232 level-shifter.

The receipt: Check the voltage before connecting. 3.3V to USB-UART bridge = safe. 3.3V directly to DB9 RS-232 port = smoke. Read the datasheet of anything you're about to plug into.

👁️ I Do — Go Board → PC Connection

   iCE40 HX1K              FT2232H USB-UART            Host (Laptop)
  ┌────────────┐          ┌────────────────┐         ┌──────────────┐
  │            │          │                │         │              │
  │  UART TX   │──TX──────│ RX   chip   USB│─────────│ /dev/ttyUSB0 │
  │  (your RTL)│          │                │         │  pyserial    │
  │            │──RX──────│ TX           (D+)(D−)   │  minicom     │
  │            │          │                │         │              │
  │     GND    │──────────│ GND           VBUS──     │ (5V USB power)│
  └────────────┘          └────────────────┘         └──────────────┘
    3.3V TTL                                           115200 baud
    
My thinking: Three wires: TX, RX, GND. The FT2232H is pre-wired on the Go Board PCB, so you don't solder anything. On the laptop, the serial port appears as soon as you plug in USB. screen /dev/ttyUSB0 115200 opens it; screen -X quit closes it (or Ctrl-A K).

🤝 We Do — Transmitting “HELLO”

module hello_tx (
    input wire i_clk, i_btn, output wire o_tx
);
    // Character ROM: 5 characters (H, E, L, L, O), repeated forever
    reg [7:0] rom [0:4];
    initial begin
        rom[0] = 8'h48; rom[1] = 8'h45; rom[2] = 8'h4C;
        rom[3] = 8'h4C; rom[4] = 8'h4F;
    end

    // Slow timer — one character per ~0.5 s so you can watch in terminal
    reg [23:0] r_timer;
    reg [2:0]  r_idx;
    reg        r_send;

    always @(posedge i_clk) begin
        r_send <= 1'b0;
        r_timer <= r_timer + 1;
        if (r_timer == 24'd12_500_000) begin
            r_timer <= 0;
            r_send  <= 1'b1;
            r_idx   <= (r_idx == 3'd4) ? 3'd0 : r_idx + 1'b1;
        end
    end

    uart_tx #(.CLKS_PER_BIT(217)) u_tx (
        .i_clk(i_clk), .i_reset(1'b0),
        .i_valid(r_send), .i_data(rom[r_idx]),
        .o_busy(), .o_tx(o_tx)
    );
endmodule
Together: Character ROM (5 entries), slow timer (0.5s), UART TX instance. The timer pulses r_send every 0.5s; the UART TX latches the current character and transmits; the index advances to the next character. Entire composition: ~25 lines of new code, using your UART as a ready-to-use building block.

🧪 You Do — Diagnose the Garbage

You flash the board. Terminal shows: æ²■µÌ instead of HELLO.

Most likely cause: Baud rate mismatch. Your Verilog parameter CLKS_PER_BIT doesn't match screen's rate. Check:
  • Verilog: CLKS_PER_BIT = 217 ← assumes 25 MHz / 115200
  • Actual Go Board clock: confirm 25 MHz (some boards are 12 MHz)
  • Screen command: screen /dev/ttyUSB0 115200 (not 9600)
  • Terminal's 8N1 setting (most defaults are correct, but verify)
Other possibilities: wrong clock constant in your PCF/constraints file; Verilog using an LSB/MSB convention the terminal doesn't expect; TX and RX wires swapped in the constraints.
▶ LIVE DEMO

Your Go Board Says HELLO

~5 minutes — end-to-end

▸ COMMANDS

cd labs/week3_day11/ex4_hello/
make prog
# in another terminal:
screen /dev/ttyUSB0 115200
# (Ctrl-A K to quit)

# Or via Python:
python3 rx_display.py

▸ EXPECTED TERMINAL

HELLOHELLOHELLOHELLO
HELLOHELLOHELLOHELLO
HELLOHELLOHELLOHELLO
HELLOHELLO...

▸ THE MOMENT

Character-by-character HELLO streams into your terminal. Your Verilog is driving a real signal into your laptop. Take a moment — most students will remember this moment of their FPGA career.

🤖 Check the Machine

Ask AI: “My UART TX is outputting garbage on the terminal. What are the top five causes and how do I diagnose each?”

TASK

AI diagnoses a UART bring-up bug.

BEFORE

Predict: baud mismatch, CLKS_PER_BIT off-by-one, bit order (LSB/MSB), inverted idle, wrong parity.

AFTER

Strong AI gives concrete diagnostic steps (scope the line, check idle level). Weak AI just lists causes without diagnostics.

TAKEAWAY

A good AI debugging session produces an ordered diagnostic sequence, not just a list of possibilities.

Pre-Class Self-Check

Q1: At 25 MHz clock and 115200 baud, what's CLKS_PER_BIT (rounded)?

25_000_000 / 115_200 = 217.01. Round to 217. Actual baud: 25e6/217 = 115207.4 — 0.006% error, well within tolerance.

Q2: Why do we need a USB-UART bridge chip between the FPGA and laptop?

Laptops speak USB; FPGAs speak 3.3V TTL UART. The bridge chip translates. Without it, you'd need a cable with a TTL-to-USB adapter.

Pre-Class Self-Check (cont.)

Q3: You see random characters in your terminal. What do you check first?

Baud rate on both sides. Second: clock frequency assumption in your Verilog (did someone put 50 MHz in a parameter but the board is really 25 MHz?). Third: 8N1 framing settings.

Q4: Why transmit at 0.5 s intervals instead of full baud rate?

So humans can watch characters appear one at a time. At 115,200 baud a 5-character message completes in 434 µs — too fast to perceive. For demos, slow the pacing. For real data, send at full rate.

🔗 End of Day 11

Tomorrow: Receiving Bytes

Day 12 · UART RX · SPI · IP Integration · Week 3 Capstone

▸ WHY THIS MATTERS NEXT

You transmit. Day 12 is receiving — harder than transmitting, because you don't control the timing. You'll learn 16× oversampling (the standard UART RX trick), start-bit detection, and then round out Week 3 with SPI (a different serial protocol) and a full IP-integration methodology. By Friday: bidirectional UART + SPI = you can talk to any peripheral in the embedded world.