SPI-configurable UART controller with safety monitoring, sticky fault handling, safe-state control, interrupt generation, and self-checking Verilog verification.
This repository documents the RTL design and verification phase only.
A future separate repository will use this verified RTL as the starting point for ASIC implementation: RTL synthesis, STA, physical design, and GDSII generation.
UART Safety Controller ASIC is a digital RTL project that implements a configurable UART communication controller with safety-oriented supervision logic. The design combines UART transmit and receive datapaths, SPI-accessible control registers, CRC-8 support, watchdog supervision, sticky fault latching, safe-state control, and interrupt generation.
The goal of this repository is to show the front-end design foundation: modular Verilog RTL, clean top-level integration, block-level verification, and an integrated self-checking system test. It does not claim synthesis, timing closure, physical design, DRC/LVS, tapeout, or GDSII completion.
This project is intended to lead naturally into a second ASIC implementation repository. That future repository will take this RTL through the SKY130/OpenLane implementation flow.
Many UART examples stop at basic transmit and receive logic. This project extends the idea into a more system-oriented controller: firmware can configure the block through SPI, received data can be buffered, data integrity can be checked with CRC-8, and abnormal behavior can be escalated through watchdog, fault, safe-state, and interrupt logic.
The result is a compact RTL project that demonstrates more than a single protocol block. It shows how communication logic, register-mapped control, CDC-aware inputs, and safety-oriented supervision can be integrated and verified as one digital subsystem.
The diagram below shows the complete controller at a system level. The main idea is that an external processor configures the UART through SPI, while the internal logic handles serial communication, buffering, supervision, fault response, and interrupt signaling.
The SPI Register Interface receives external SPI transactions and converts them into internal register reads and writes. This lets firmware configure the controller without directly controlling internal RTL signals.
The Register Bank stores configuration values and exposes status information. It controls baud rate, parity mode, watchdog behavior, CRC options, interrupt behavior, fault clearing, and safe-state behavior.
The UART datapath handles serial transmit and receive operation. The baud generator creates timing pulses, the UART TX and RX FSMs serialize and reconstruct bytes, the RX synchronizer protects against asynchronous RX input transitions, and the RX FIFO stores received bytes until software reads them.
The safety path supervises operation. The watchdog detects missing activity, the fault latch records error events, the safe-state controller reacts to critical faults, and the IRQ controller notifies the external processor.
- SPI-configurable UART controller
- UART transmitter and receiver
- Programmable baud-rate generator
- 2-flop RX synchronizer for CDC protection
- RX FIFO buffering
- CRC-8 integrity checking
- Watchdog supervision
- Sticky fault handling
- Safe-state controller
- Interrupt generation
- Self-checking Verilog verification
- 12 integrated RTL modules: communication, configuration, buffering, safety, and interrupt logic.
- 13 self-checking testbenches: each block has directed PASS/FAIL verification.
- UART + SPI interfaces: serial datapath controlled through a register-mapped SPI interface.
- Safety-oriented architecture: watchdog, sticky fault, safe-state, and IRQ response logic.
- Parameterized RTL blocks: reusable baud, FIFO, UART, and watchdog-style building blocks.
- Top-level integration completed: full-system simulation passes with SPI configuration, UART TX, fault, safe-state, and IRQ coverage.
- Future ASIC foundation: this repo is ready to feed a separate SKY130/OpenLane implementation repository.
The architecture is organized around small RTL blocks with clear ownership. Communication logic, configuration logic, buffering logic, and safety logic are separated so each block can be verified independently before system integration.
The design also follows an ASIC-oriented style: synchronous sequential logic, explicit reset behavior, simple module interfaces, parameterized widths where useful, and dedicated control/status registers. This makes the RTL easier to review, verify, and later prepare for synthesis in a separate ASIC implementation flow.
- The external processor configures the controller through the SPI Register Interface.
- Configuration values are stored inside the Register Bank.
- The baud generator creates
baud_tickpulses from the programmable divider. - When software writes
TX_DATA, the UART transmitter starts serializing the byte. - The UART TX FSM sends the start bit, data bits, optional parity bit, and stop bit.
- Incoming UART data first passes through a 2-flop RX synchronizer before reaching the receiver.
- The UART receiver samples the synchronized serial input and reconstructs received bytes.
- CRC logic can be used to check data integrity when CRC support is enabled.
- The RX FIFO stores received bytes so software can read them later through the register interface.
- The watchdog supervises system activity and asserts a timeout if it is not serviced.
- Fault conditions are captured by the sticky fault latch.
- Critical faults can force the controller into safe state.
- The IRQ controller asserts an interrupt when enabled events require processor attention.
This flow separates control from datapath behavior. Firmware configures and observes the design through registers, while the RTL performs timing-sensitive UART, buffering, and safety operations in hardware.
CPU writes CTRL register
|
v
Controller enabled
|
v
CPU programs baud divider
|
v
CPU writes TX_DATA = 0x55
|
v
UART TX FSM starts
|
v
Serial bits are transmitted on UART TX
|
v
Transmission completes
|
v
TX_BUSY clears
In this example, the processor enables the controller, selects the baud timing, and writes a byte into TX_DATA. The UART transmitter then handles the actual serial framing and bit timing without further processor intervention.
Watchdog is enabled
|
v
Watchdog is not serviced before timeout
|
v
Watchdog timeout asserts
|
v
Fault is latched
|
v
safe_state asserts
|
v
IRQ is generated
This path demonstrates why the safety logic is separate from the normal UART datapath. Even if communication logic is idle or stalled, the watchdog and fault-handling path can still record the condition and notify the processor.
The data-flow diagram focuses on how information moves through the controller. It separates configuration, transmit, receive, and safety/interrupt behavior so the reader can trace each path independently.
The design has four main paths:
- Configuration path: SPI writes and reads configure control/status registers.
- Transmit path: software writes
TX_DATA, which drives the UART transmitter. - Receive path: asynchronous UART RX is synchronized, decoded, and buffered in the RX FIFO.
- Safety/interrupt path: watchdog and error conditions set sticky faults, assert safe state, and generate IRQ.
The register map is the software-visible control surface of the design. Firmware uses these addresses to configure the controller, start transmissions, read received data, inspect status, and clear latched faults.
| Address | Register | Purpose |
|---|---|---|
0x00 |
CTRL |
Enable, parity, loopback, CRC, and IRQ control |
0x01 |
BAUD_LO |
Baud divider low byte |
0x02 |
BAUD_HI |
Baud divider high byte |
0x03 |
STATUS |
TX/RX status, fault state, and safe-state status |
0x04 |
TX_DATA |
UART transmit data register |
0x05 |
RX_DATA |
UART receive data register |
0x06 |
FAULT |
Sticky fault register with write-1-to-clear behavior |
0x07 |
WDT_RELOAD |
Watchdog reload value |
0x08 |
WDT_CTRL |
Watchdog enable/kick/control register |
0x09 |
SAFE_CFG |
Safe-state configuration and clear control |
0x0A |
IRQ_MASK |
Interrupt mask register |
0x0B |
CRC_CFG |
CRC configuration register |
CTRL controls the main operating mode, including enable, parity, loopback, CRC, and IRQ behavior. STATUS exposes TX/RX state and fault/safe-state information. FAULT is sticky and uses write-1-to-clear semantics so fault events remain visible until firmware explicitly acknowledges them.
Each RTL block was designed and verified independently before top-level integration.
The image below is a documentation-oriented view of the top-level module and the RTL blocks instantiated beneath it.
The Mermaid diagram provides the same hierarchy in a GitHub-rendered format.
flowchart TD
top[ uart_safety_controller_top.v ]
top --> spi[ spi_reg_if.v ]
top --> regs[ reg_bank.v ]
top --> baud[ baud_gen.v ]
top --> tx[ uart_tx.v ]
top --> sync[ rx_synchronizer.v ]
top --> rx[ uart_rx.v ]
top --> fifo[ fifo.v ]
top --> crc[ crc8.v ]
top --> wdt[ watchdog.v ]
top --> fault[ fault_latch.v ]
top --> safe[ safe_state_ctrl.v ]
top --> irq[ irq_ctrl.v ]
| Module | Description |
|---|---|
baud_gen.v |
Programmable baud tick generator. It divides the system clock into UART timing ticks used by the TX and RX logic. |
uart_tx.v |
UART transmitter FSM. It frames parallel bytes into serial UART start, data, parity, and stop bits. |
uart_rx.v |
UART receiver FSM. It samples the serial RX stream and reconstructs received bytes. |
rx_synchronizer.v |
2-flop synchronizer for asynchronous RX input. It reduces metastability risk before RX data enters synchronous logic. |
fifo.v |
Small parameterized FIFO. It buffers received bytes so software does not need to read each byte immediately. |
crc8.v |
CRC-8 polynomial 0x07 engine. It supports integrity checking for data paths that need error detection. |
watchdog.v |
Watchdog timeout timer. It detects missing service activity and raises a timeout condition. |
fault_latch.v |
Sticky fault register with write-1-to-clear behavior. It preserves fault events until firmware clears them. |
safe_state_ctrl.v |
Safe-state controller. It asserts safe state when critical fault conditions are active. |
irq_ctrl.v |
Interrupt request logic. It combines enabled events into a processor-visible IRQ output. |
reg_bank.v |
SPI-accessible control/status registers. It is the bridge between firmware-visible configuration and internal RTL control signals. |
spi_reg_if.v |
SPI slave register interface. It converts SPI transactions into internal register bus operations. |
uart_safety_controller_top.v |
Integrated top-level system. It connects the communication, register, safety, and interrupt blocks. |
Verification was performed using a bottom-up strategy. Each RTL module was independently verified using a dedicated self-checking Verilog testbench. The testbenches check expected behavior and print PASS/FAIL results, which makes regressions easier to identify from the simulation transcript.
Only after block-level verification were the modules integrated into uart_safety_controller_top.v. The final integration test verifies that SPI configuration, register writes, UART transmission, watchdog timeout, fault latching, safe-state entry, and interrupt generation work together in the full system.
| Testbench | Status |
|---|---|
baud_gen_tb |
PASS |
uart_tx_tb |
PASS |
rx_synchronizer_tb |
PASS |
uart_rx_tb |
PASS |
fifo_tb |
PASS |
crc8_tb |
PASS |
watchdog_tb |
PASS |
fault_latch_tb |
PASS |
safe_state_ctrl_tb |
PASS |
irq_ctrl_tb |
PASS |
reg_bank_tb |
PASS |
spi_reg_if_tb |
PASS |
uart_safety_controller_top_tb |
PASS |
The screenshot below captures the integrated system simulation passing. It is useful evidence that the top-level testbench completed the expected sequence rather than only compiling individual blocks.
The integrated top-level simulation verifies:
- Reset behavior and top-level defaults
- SPI write to
CTRL - SPI write to baud divider registers
- SPI write to
TX_DATA - UART TX start and return to idle
- Watchdog timeout generation
fault_activeassertionsafe_stateassertion- IRQ assertion
The waveform below shows the important system-level safety sequence. It connects the transcript PASS result to visible signal behavior inside the integrated RTL.
The waveform shows SPI/register configuration first, followed by UART TX activity. The watchdog then times out, which drives the sticky fault path. After the fault is latched, fault_active, safe_state, and irq assert as expected.
Create the simulation output directory:
mkdir -p simRun the full integrated system simulation with Icarus Verilog:
iverilog -o sim/uart_safety_controller_system_tb.out \
rtl/uart_safety_controller_top.v \
rtl/spi_reg_if.v \
rtl/reg_bank.v \
rtl/irq_ctrl.v \
rtl/baud_gen.v \
rtl/uart_tx.v \
rtl/rx_synchronizer.v \
rtl/uart_rx.v \
rtl/fifo.v \
rtl/crc8.v \
rtl/watchdog.v \
rtl/fault_latch.v \
rtl/safe_state_ctrl.v \
tb/uart_safety_controller_top_tb.v
vvp sim/uart_safety_controller_system_tb.outThe system waveform was also inspected using ModelSim/Questa. The provided sim/run_system.do script compiles the integrated testbench, loads key top-level and internal signals, and runs the simulation.
This repository contains the RTL design, verification testbenches, simulation script, waveform captures, and documentation images for the design-and-verification phase.
uart-safety-controller-asic/
|-- rtl/
|-- tb/
|-- sim/
|-- images/
|-- waves/
|-- README.md
|-- LICENSE
`-- .gitignore
Future implementation directories such as reports/, synth/, sta/, and openlane/ belong in the next ASIC implementation repository, not this RTL design-and-verification repository.
- RTL modules implemented
- Block-level testbenches implemented
- SPI register interface verified
- Register bank verified
- UART TX verified
- UART RX verified at block level
- FIFO verified
- CRC-8 verified
- Watchdog, fault latch, and safe-state logic verified
- Integrated top-level system simulation passed
- ASIC implementation repository
- Yosys synthesis in future repository
- OpenSTA timing analysis in future repository
- Gate-level simulation in future repository
- OpenLane physical design in future repository
- DRC/LVS in future repository
- GDSII generation in future repository
This RTL project is the foundation for a second repository that will demonstrate the ASIC implementation flow using SKY130 and OpenLane. The future repository will start from this verified RTL and focus on implementation analysis rather than functional RTL bring-up.
Planned future ASIC implementation work:
- Logic synthesis
- SKY130 standard-cell mapping
- OpenSTA timing analysis
- Gate-level simulation
- Floorplanning
- Placement
- Clock Tree Synthesis
- Routing
- DRC checks
- LVS checks
- GDSII generation
These steps are planned future work and are not presented as completed in this repository.
Skills demonstrated:
- Modular Verilog RTL design
- UART transmit/receive FSM implementation
- SPI-controlled register-mapped configuration
- CDC-aware RX synchronization
- FIFO buffering and CRC-8 integrity checking
- Watchdog supervision and sticky fault handling
- Safe-state and interrupt-control logic
- Self-checking block-level verification
- Integrated top-level system verification
- ASIC implementation readiness for a future SKY130/OpenLane flow





