This project focuses on the development of a bare-metal UART driver in Embedded C for a RISC-V processor using register-level programming and Memory-Mapped I/O (MMIO).
The driver provides UART initialization, register-level access, polling-based transmit and receive services, software queue management, interrupt support, error monitoring, and driver status management.
The implementation was validated using UART loopback testing and host-side test cases.
- Develop a UART driver using Embedded C
- Implement register-level programming using Memory-Mapped I/O
- Configure and initialize UART hardware registers
- Implement transmit and receive functionality
- Implement polling-based UART communication
- Implement software TX/RX queue management
- Support UART interrupt configuration and handling
- Monitor UART error conditions
- Validate driver functionality using loopback and test cases
- Embedded C
- C Programming
- RISC-V Processor
- UART
- Memory-Mapped I/O (MMIO)
- Register-Level Programming
- FIFO
- Polling
- Interrupts
- Bare-Metal Programming
- GCC
- Linux / Ubuntu
The driver initializes the UART device by:
- Mapping the UART register space
- Configuring the device base address
- Resetting TX and RX FIFOs
- Clearing and checking UART status
- Initializing software TX/RX queues
- Verifying the post-reset state
- Enabling interrupts when required
- Marking the driver as ready
The driver accesses UART hardware registers directly through memory-mapped addresses using volatile register read/write operations.
*(volatile uint32_t *)(base + offset)This provides low-level communication between the software driver and UART hardware.
The transmit service:
- Checks UART status
- Checks TX FIFO availability
- Writes data to the TX FIFO
- Monitors FIFO status
- Returns the number of transmitted bytes
The receive service:
- Monitors RX data availability
- Reads received bytes from the RX FIFO
- Transfers received data into the software RX queue
- Tracks received byte statistics
The driver uses status-register polling to monitor UART TX/RX FIFO conditions and perform data transfer.
The driver also provides interrupt-related functionality including:
- TX interrupt enable
- RX interrupt enable
- Interrupt service handling
- TX queue service
- RX queue service
The driver monitors UART error conditions including:
- Overrun errors
- Frame errors
- Parity errors
Error statistics are maintained by the driver.
The driver provides:
- TX FIFO reset
- RX FIFO reset
- FIFO status monitoring
- FIFO recovery
- FIFO-based transmit and receive services
The driver uses a memory-mapped UART register interface:
| Offset | Register | Description |
|---|---|---|
0x00 |
RX_FIFO | Receive data register |
0x04 |
TX_FIFO | Transmit data register |
0x08 |
STAT_REG | UART status register |
0x0C |
CTRL_REG | UART control register |
The register map and bit definitions are implemented according to the custom UART core used in this project.
Application / Test Program
│
▼
UART Driver API
│
▼
Memory-Mapped I/O
│
▼
UART Registers
│ │
▼ ▼
TX FIFO RX FIFO
│ │
▼ ▼
UART TX UART RX
│ │
└────┬─────┘
▼
Serial Communication
│
▼
Loopback / Testing
The driver was tested using:
- UART loopback testing
- Transmit and receive validation
- FIFO boundary testing
- NULL argument testing
- Zero-length input testing
- Software queue overflow testing
- UART error-condition testing
- Driver ready/not-ready state testing
- Corner-case and boundary-condition testing
The test suite reports PASS / FAIL results and uses the process exit status to indicate test success.
The loopback test validates the transmit and receive path.
Test Data
│
▼
UART TX FIFO
│
▼
UART TX
│
▼
TX → RX Loopback
│
▼
UART RX
│
▼
UART RX FIFO
│
▼
Received Data
│
▼
Compare TX and RX
│
▼
PASS / FAIL
The loopback test transmits test bytes, receives them back through the RX path, compares transmitted and received data, and reports the result.
The driver includes a host-side simulated hardware model for testing without physical UART hardware.
The test environment models UART register behavior and FIFO operations to validate driver functionality.
Example build command:
gcc -Wall -Wextra -std=c11 \
axi_uart_lite.c test_axi_uart_lite.c \
-o test_axi_uart_liteRun:
./test_axi_uart_liteThe test suite checks boundary conditions, invalid arguments, FIFO limits, software queue behavior, error states, and other corner cases.
UART-Driver-Development-RISC-V-Processor/
│
├── axi_uart_lite.c
├── axi_uart_lite.h
├── loopback_test.c
├── test_axi_uart_lite.c
├── test_corner_cases (1).c
├── README.md
└── LICENSE
| File | Description |
|---|---|
axi_uart_lite.c |
UART driver implementation |
axi_uart_lite.h |
UART driver declarations, register definitions, structures and APIs |
loopback_test.c |
UART TX/RX loopback test |
test_axi_uart_lite.c |
Driver functionality and corner-case test suite |
test_corner_cases (1).c |
Additional boundary and corner-case tests |
README.md |
Project documentation |
LICENSE |
Apache License 2.0 |
- UART driver development
- Embedded C programming
- RISC-V processor concepts
- Memory-Mapped I/O
- Register-level programming
- FIFO-based communication
- Polling-based communication
- Interrupt handling
- Device driver architecture
- Error monitoring
- Software queue management
- Driver testing and debugging
- UART loopback validation
- Add configurable baud-rate support if supported by the target UART hardware
- Add hardware-specific RISC-V build and deployment configuration
- Integrate with a complete RISC-V embedded platform
- Add automated test execution
- Improve timeout handling for polling operations
This project is licensed under the Apache License 2.0.

