517 uart

517 : uart

Design render

UART (Universal Asynchronous Receiver + Transmitter) : A Tiny Tapeout Project

How it works

This project implements a simple UART (Universal Asynchronous Receiver + Transmitter). It's capable of independently transmitting and receiving 8-bit serial data (8-N-1).

Field Value
Data bits 8
Parity None
Stop bits 1

The design is split into four modules:

  • tt_um_blonghi_uart - the top-level wrapepr that maps pins to signals and instantiates the 3 following modules.

  • baud_rate_gen - generates two enable ticks from the system clock. 'tx_counter' wraps around at 5119, producing 'tx_enb' (one pulse per baud period). 'tx_counter' resets every 'tx_sync' pulse (the start of a new frame), keeping TX bit timing aligned. 'rx_counter' wraps around at 319 (1/16th of 'tx_counter's range) This produces 'rx_enb' at 16x the rate to allow the receiver to oversample and locate the center of each incoming bit. 'rx_counter' resets every 'rx_sync' pulse (start-bit detection), keeping RX sampling aligned.

  • transmitter - FSM that on write request serializes an 8-bit byte onto the tx line as: start bit, 8 data bits (LSB first), then a stop bit.

stateDiagram-v2
direction LR
    [*] --> IDLE
    IDLE --> START: wr_enb
    START --> DATA: tx_enb
    DATA --> STOP: 8 bits transmitted
    STOP --> IDLE: tx_enb
  • receiver - FSM that watches the rx line for a falling edge (start bit) then samples 8 data bits at the center od each bit period (16x oversampling to find bit-center), then checks for the stop bit and pulses rx_valid for one cycle with the received byte on rx_data.
stateDiagram-v2
direction LR
    [*] --> IDLE
    IDLE --> START: falling edge
    START --> DATA: rx_enb
    DATA --> STOP: 8 bits received
    STOP --> IDLE: valid stop bit
Limitations
  • No parity bit.
  • If there is a bad frame, it is simply never latched with no downstream indication the error occurred.
  • rx_data is split between two buses: uio_out and uo_out

Pin mappings

TX (transmit)

Signal Pin Direction
tx_data ui_in[7:0] input
wr_enb uio_in[0] input
tx uo_out[0] output

RX (receive)

Signal Pin Direction
rx uio_in[1] input
rx_valid uo_out[1] output
rx_data[1:0] uo_out[3:2] output
rx_data[7:2] uio_out[7:2] output

Note: keep in mind that rx_data is split across two separate output buses

Unused / fixed

Signal Pin Value
uio_in[7:2] unused
uo_out[7:4] tied to 0
uio_out[1:0] tied to 0
uio_oe fixed 8'b1111_1100

Note: uio_oe is fixed since I needed to accommodate 6 continuous output pins for the upper bits of rx_data and 2 input pins for wr_enb and rx.

Baud rate assumptions

My Main Assumptions

  • System clock: 49.152 MHz
  • Target baud rate: 9600
  • TX: exactly 5120 clock cycles per bit
  • RX: 16x oversampling
  • RX: exactly 320 clock cycles per tick
  • RX samples near tick 8
The Math:

The clock runs at $49,152,000$ Hz (not a round 50 MHz, see note below), which means $49,152,000$ clock cycles per second.

9600 baud means $9600$ bits per second.

So the number of clock cycles in one bit is $\frac{49,152,000}{9600} = 5120$.

For RX, I use 16x oversampling, meaning there are 16 RX ticks for every bit: $\frac{5120}{16} = 320$.

So the RX counter uses exactly 320 clock cycles per RX tick.

The receiver then counts these 16 RX ticks and samples the actual RX signal around tick 8.

Note: 50,000,000 / 9600 isn't a whole number (5208.33), which causes a small timing error. 49,152,000 was chosen specifically because it divides evenly at both levels: once by 9600 for the bit period, and again by 16 for the RX tick.

This all came out of FPGA testing, where a rounding mismatch (27MHz clock, 9600 baud) caused decode errors until a cleanly dividing rate was used instead.

How to test

RTL simulation

cd test
pip install -r requirements.txt
make -B

GLS

Requires one-time PDK setup which can be a headache ;-;.

Once the PDK is set up, this is the whole sequence. Run it again every time you change a .v file, since hardening makes a new netlist and the copy sitting in test/ goes stale.

export PDK_ROOT=/path/to/IHP-Open-PDK
export PDK=ihp-sg13cmos5l
export LIBRELANE_TAG=3.0.0rc1

# from the repo root
./tt/tt_tool.py --harden --ihp

cd test
make -B                  # RTL simulation

TOP_MODULE=$(cd .. && ./tt/tt_tool.py --print-top-module --ihp)
cp ../runs/wokwi/final/nl/$TOP_MODULE.nl.v gate_level_netlist.v

make -B GATES=yes        # gate-level simulation

IO

#InputOutputBidirectional
0tx_data[0]txwr_enb
1tx_data[1]rx_validrx
2tx_data[2]rx_data[0]rx_data[2]
3tx_data[3]rx_data[1]rx_data[3]
4tx_data[4]rx_data[4]
5tx_data[5]rx_data[5]
6tx_data[6]rx_data[6]
7tx_data[7]rx_data[7]

Chip location

Controller Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Analog Mux Mux Mux Mux Mux Mux Mux Mux tt_um_chip_rom (Chip ROM) tt_um_factory_test (Tiny Tapeout Factory Test) tt_um_ieee_LDO (LDO) tt_um_chip_ieee_analog (IEEE Bandgap Reference) tt_um_snn_voice_calculator_mauro_ciccone (snn-voice-calculator) tt_um_hx2003_delay (4 Channel - 32 Tap Programmable Delay with Delay Locked Loop Calibration) tt_um_adxl362_test (tt_um_adxl362_test) tt_um_larsnit_cfar (1D CA/GO/SO CFAR radar detector) tt_um_abeccari_swsynth (Sine Wave Synthesizer) tt_um_dpi_adexp (AdExp DPI Neuron ) tt_um_140oo041_fpu130 (FPU-130) tt_um_blonghi_uart (uart) tt_um_directsgg_mini_proceo_8bit (Mini 8-bit Processor) tt_um_umaece1982_lfsr (Low-Power LFSR-Based Test Pattern Generator) tt_um_deploy_timer (launch deployment timer) tt_um_urish_simon (Simon Says memory game) tt_um_nimelli_kinematic_wave_engine (Kinematic Wave Engine) tt_um_multi_seg_monitor (Multi Segment Monitor) tt_um_UART_TX (project) tt_um_crc8_lfsr (CRC-8 Serial LFSR) tt_um_tinynpu4 (TinyNPU4) tt_um_alu_bns (6-bit multi function ALU ( eldawly_V2) ) tt_um_echoworld424_tpv (Timing-Prediction Test Vehicle) tt_um_gyro_lockin (Laser Gyro Lock-in Readout Core) tt_um_josue_olivos_sar_adc (4-Bit Charge-Redistribution SAR ADC Controller) tt_um_flower (VGA Flower) tt_um_vperumal_l1_fabric (Scalable Banked L1 Memory Fabric for Edge AI) tt_um_preinception_top (Preinception: Simple Compute Accelerator) tt_um_italu (iTALU: Interactive Testable Arithmetic Logic Unit) tt_um_neuron (4-Input Signed Neuron / Perceptron) tt_um_4tap_mac (4-Tap Signed MAC Unit) tt_um_mac_engine (DSP MAC Engine) tt_um_crypto_led_demo (QAMER CryptoUART: Encrypted UART with LED Status) tt_um_layernorm (LayerNorm) tt_um_ez130_8t_mystery (EZ130 8T Mystery Circuit) tt_um_sent2spi (SENT Receiver with SPI Interface) tt_um_llr_hepiarisc (Hepiarisc with SPI flash) tt_um_rebeccargb_vga_pride (VGA Pride) tt_um_hasi_ising (Oscillator Ising Machine) tt_um_c061618g2 (Circuitli C061618G2) tt_um_tiny_dram_pim (Tiny Dual-Channel DRAM-PIM Controller + PU) tt_um_Tbilisi_CORDIC_Engine (Tbilisi CORDIC Engine) tt_um_rahulmascarenhas_folded_nn (Frozen ternary backbone + loadable head) tt_um_miniMAC (miniMAC_IHP26b) tt_um_rumcajs (IEEE DOORSH) tt_um_sg13g2_mystery (SG13G2 Mystery Circuit) tt_um_ULSR88 (ULSR demo) tt_um_ez130_7t_mystery (EZ130 7T Mystery Circuit) tt_um_tinyopt4 (ieee_tt_tinyopt4) tt_um_vga_example (IEEE VGA Animated Beach) tt_um_hyphen133_drone_detection (IEEE Acoustic Drone Detector) tt_um_nuatlabs_fifo_pwm (Async FIFO with CDC + PWM Peripheral) tt_um_nuatlabs_uart (8N1 UART Transceiver) tt_um_eeg_threshold_detector (IEEE Digital EEG Threshold Event Detector) tt_um_smart_traffic (Smart Traffic Light Controller) tt_um_94442024_mini_cpu (Mini 8-bit Accumulator CPU) tt_um_wokwi_475369131246576641 (IEEE_UPB_TT_1) tt_um_aion (AION) tt_um_rebeccargb_hardware_utf8 (Hardware UTF Encoder/Decoder) tt_um_rebeccargb_universal_decoder (Universal Binary to Segment Decoder) tt_um_rebeccargb_intercal_alu (INTERCAL ALU) tt_um_flappy_bird (IEEE Flappy Bird VGA Game) tt_um_oryan01_alu (ALU CASS PUCV) tt_um_S4xU4 (S4xU4) tt_um_vga_ca (Space CA) tt_um_llr_simplenpu (simple SPI flash streaming NPU) tt_um_pucv_pspwm (3LFCC PS-PWM Modulator) tt_um_yuri_fpga (Tiny FPGA) tt_um_mikailgedik_inverted_inverters (Inverted inverters) tt_um_esauqch_hamming74 (Hamming(7,4) encoder/decoder (IEEE)) tt_um_hackin7_analog_experiments (TinyAnalogExperiments) tt_um_snake (snake game) tt_um_mini_kraken (Kraken IO Subprocessor) tt_um_fabien_pio (AstraPIO) tt_um_chiplab (ChipLab) tt_um_wokwi_475490677474407425 (Tiny_Divider) tt_um_c061618g2tr (Circuitli C061618G2TR) tt_um_catalinlazar_nanopio (nanoPIO) tt_um_catalinlazar_uart_spi_i2c_bridge (UART-SPI-I2C Bridge) tt_um_enzonappi_sent_i2c (SENT to I2C bridge) tt_um_kush1434_proof (Proof) tt_um_schwallsunk_signal_discriminator (Highspeed voltage discriminator) tt_um_tiarinix_ttihp_verilog_template (8-bit educational SAP-style CPU) tt_um_vga_glyph_mode (BOOTCAMP) tt_um_GiulioGirelli_packet_processor (Configurable Low-Latency Match-Action Packet Processor) tt_um_vga_tictactoe (Tic Tac Toe) tt_um_vga_dvd_player (DVD player) tt_um_clea_katseye_rain (KATSEYE) tt_um_romd_uart_hello (UART Hello World) tt_um_vga_snake (CDM PYTHON GAME) tt_um_vga_slot_machine (tt_um_vga_slot_machine) tt_um_jet_seq8b (SEQ8 Programmable Sequencer) tt_um_kibo_leak_inspect (KIBO Leak-Inspection Target Controller (VGA)) tt_um_endless_runner (Endless Runner) tt_um_omega_infinity_kaoru (OMEGA INFINITY KAORU 3D Metal Grid Processor) tt_um_nikleberg_mixer (Mixer) tt_um_lahnb_sgdma (TinyDMA: A Descriptor-Based Dual-PSRAM Memory Mover) tt_um_gstj_lockin (Digital IQ Lock-in (IEEE)) tt_um_benpayne_ps2_decoder (PS/2 Keyboard Decoder for 68k) tt_um_cass_s_ui_neuron_lif (Neurona LIF con Aprendizaje STDP Dinamico (IEEE)) tt_um_vga_glyph_mode_CDM_Matrix (CDM Matrix) tt_um_qd39l_xor_stream (Fixed-ROM XOR Stream Engine) tt_um_conv3x3 (3x3 Clock Rate Streaming Input Convolution Engine) tt_um_mc14500b_soc_extended (MC14500B Extended 1-bit Microcontroller SoC) tt_um_vga_hypno_spiral (tt_um_vga_hypno_spiral) tt_um_mattizen_morse_tree (Morse Tree LED Decoder) tt_um_CDM (Colegio de Muntinlupa DVD-like Display) tt_um_romd_uart_loader (UART SPI RAM Loader) tt_um_TscherterJunior_stapel_geraet (stapel gerät) tt_um_das2225_dna_accel (DNA_Accel) tt_um_tinysoc (TinySoC) tt_um_barrel_shifter (Barrel Shifter) tt_um_approx_mac_coprocessor (Approximate DSP: Time-Multiplexed MAC Coprocessor) tt_um_joesagents_market_split_oracle (Market-split oracle) tt_um_mgpauly1458_ringmeter (Ring oscillator frequency meter) tt_um_pettit_prism_lite (PRISM with Risc-V (TinyQV) SoC) tt_um_workshop_cpu (IEEE Workshop Simple CPU) tt_um_algofoogle_analog_junk (Simple comparator + 2 DACs analog layout in a 1x1 tile) tt_um_lkhanh_cordic (TinyQV SoC (Dual Memory Backend)) tt_um_4x4npu (4x4NPU: Dual-Lane INT4 Neural Accelerator) tt_um_abiaselli_izh_bridge_3x2 (Izhikevich event bridge (4 contexts)) tt_um_fabulous_ihp_26b (Tiny FABulous FPGA) tt_um_zanderivo_voronoi (Four-Metric VGA Nearest-Prototype Visualizer)