Goal: Understand the UART protocol (8N1, baud), translate it to RTL (TX, RX, baud gen), and verify with a basic (non-UVM) directed testbench.
← Previous: Module 2: Methodology (Part 2) | Next: Module 4: UART UVM+SV →
Modules 3–8 are learning modules, not just exercises. Before running commands:
- Read the detailed UART learning guide: UART_LEARNING_GUIDE.md — what UART is, what kind of protocol (serial, asynchronous, point-to-point), how it works (frame format, baud rate, TX/RX), timing, and where it’s used.
- Read the protocols + UVM overview: LEARNING_GUIDE_PROTOCOLS_AND_UVM.md — Part B § 4. When to Use Baseline vs UVM (why we start with a baseline testbench before UVM).
Then return here and follow Overview → Topics Covered → Example → Exercises.
- Module doc: module3/README.md
- Example: module3/examples/uart_baseline/ — UART TX/RX RTL + basic loopback test (no UVM)
Quick run (from repo root):
cd module3/examples/uart_baseline
make runOr: ./scripts/module3.sh --run
Module 3 is the first protocol module:
- UART protocol: 8N1 framing, start/stop bits, baud rate.
- RTL:
uart_tx,uart_rx,baud_gen— translating protocol to RTL (same methodology as Module 1). - Basic testbench: Directed test (loopback TX→RX, send bytes, check received bytes). No UVM — UVM for UART is Module 4.
- UART 8N1: Start bit (0), 8 data bits (LSB first), stop bit (1); idle high.
- Baud rate: Role of a baud generator (clock divider → baud_tick).
- RTL architecture: TX (parallel→serial, shift per baud_tick), RX (start detect, sample, data_valid), baud_gen (divider).
- Basic TB: Loopback, directed stimulus, and checking without UVM.
- Module 1 (spec → RTL) and Module 2 (basic TB → UVM, toolchain).
- Verilator, Make, C++ compiler. No UVM required for the uart_baseline example.
- Signals: Serial line (tx/rx), idle high; system clock; baud_tick (one pulse per bit time).
- Frame: 1 start bit (0), 8 data bits (LSB first), 1 stop bit (1).
- Timing: One bit per baud_tick; baud_tick derived from system clock via a divider (e.g. DIVIDER = clk_freq / baud_rate).
- Framing:
idle=1→start=0→ 8 data bits (LSB first) →stop=1. - Baud math:
DIVIDER = round(clk_hz / baud). Example: 50 MHz clock, 115200 baud → divider ≈ 434. - Sampling: Basic design samples once per bit at the
baud_tick. Production UARTs often oversample (e.g., 8x or 16x) to tolerate clock drift; you can add this later by replacingbaud_tickwith an oversample tick and using a mid-bit sample. - Reset state: Line idles high, TX not busy, RX clears internal shift register,
data_validlow. - Errors:
framing_errorwhen stop bit is not high at expected sample; optionalparity_errorif parity enabled (not used in 8N1 baseline).
- Inputs:
clk,rst_n,baud_tick,start,data_in[7:0]. - Outputs:
tx,busy. - Behavior: On
start, latchdata_in, drive start bit low, then shift out bits [0:7] eachbaud_tick, then drive stop bit high for onebaud_tick. Deassertbusyafter stop bit. - Edge cases: Ignore
startwhilebusy(or queue it in a FIFO if you extend the design).
- Inputs:
clk,rst_n,baud_tick,rx. - Outputs:
data_out[7:0],data_valid(pulse),framing_error. - Behavior: Detect falling edge for start; wait one
baud_tickto sample mid-start; sample 8 bits on successivebaud_ticks; sample stop bit last. If stop is not high, raiseframing_error. Pulsedata_validfor one cycle when byte is ready. - Robustness options: Add metastability sync on
rx; add oversampling + majority vote; addrx_ready/rx_validhandshake if you later connect to a FIFO.
- Inputs:
clk,rst_n,divisor(constant parameter in the baseline). - Output:
baud_tickhigh for one cycle everydivisorclocks. - For synthesis, keep the counter simple (
divisor-1down-counter or up-counter compare). For sim, you can parametrizedivisorto speed up runs.
- Data bits: 5–9 data bits are common.
- Parity: Even/odd/mark/space; adds a parity bit before stop.
- Stop bits: 1 or 2 stop bits.
- Flow control: RTS/CTS hardware pins (outside the serial line) to throttle traffic.
- The baseline RTL assumes 8N1 with no parity and no flow control.
- baud_gen: Divides
clk; outputsbaud_tickone cycle every DIVIDER cycles. - uart_tx: On
start, loadsdata_ininto shift register; outputs one bit perbaud_tick(start, data[0:7], stop); assertsbusyduring frame. - uart_rx: Detects start bit (line low), samples one bit per
baud_tick, reconstructs byte; pulsesdata_validand outputsdata_out; can assertframing_errorif stop bit is bad.
- Loopback: Connect TX output to RX input; send bytes from TX, check they appear on RX.
- Directed test: Reset release, send 0x55 and 0xAA, wait for
data_valid, checkdata_out. Implemented intop_uart_baseline.sv(initial block) and C++ (clk, rst_n); no UVM.
| Component | Role |
|---|---|
| dut/ | uart_tx.v, uart_rx.v, baud_gen.v |
| top_uart_baseline.sv | Loopback (rx=tx), baud_gen, uart_tx, uart_rx; directed test in initial block |
| sim_main.cpp | Clock, reset; run until $finish |
Run: cd module3/examples/uart_baseline && make run
cd module3/examples/uart_baseline
make run./scripts/module3.sh --check # Environment + example dirs
./scripts/module3.sh --run # Run uart_baseline- Describe UART 8N1 frame and baud timing.
- Explain the role of uart_tx, uart_rx, and baud_gen.
- Run the uart_baseline example and interpret the loopback test.
- Ready for Module 4: UART UVM+SV verification.
-
Run uart_baseline
- Run
make runinmodule3/examples/uart_baseline. Confirm you see loopback success (e.g. bytes sent and received match).
- Run
-
Trace one byte
- Open
top_uart_baseline.svand the DUT sources. For one byte (e.g. 0x55), trace: testbench drivesstartanddata_in→ uart_tx sends start bit, 8 data bits (LSB first), stop bit → uart_rx detects start, samples 8 bits, pulsesdata_validwithdata_out. Map each step to the corresponding RTL (baud_tick, busy, etc.).
- Open
-
Baud and divider
- In the example, find the baud divisor (or clock/baud relationship). Change the baud rate (or divisor) in simulation only and re-run; confirm the test still passes if TX and RX use the same timing.
-
Optional: Waveforms
- If the Makefile generates a VCD, open it and identify: baud_tick, tx, rx, start, data_valid, data_out. Confirm one UART frame (start + 8 data + stop) per byte.
- Can describe UART 8N1 (start bit, 8 data LSB first, stop bit) and the role of baud_tick.
- Can explain the role of uart_tx, uart_rx, and baud_gen in the RTL.
- Can run uart_baseline and interpret the loopback test result.
- Ready for Module 4: UART UVM+SV verification.
After completing this module, proceed to Module 4: UART UVM+SV — full UART verification with UVM (agent, sequences, driver, monitor, scoreboard).