#ifndef _TB_UART_IO_H
#define _TB_UART_IO_H

#include <stdint.h>
#include <stdbool.h>

// Testbench UART-over-TCP MMIO (see tb_common/include/tb_constants.h).
//
// Each UART is exposed as a raw TCP byte stream (one client at a time).
// Software should poll STATUS.RX_AVAIL before reading DATA.

#ifndef TB_IO_BASE
#define TB_IO_BASE 0xC0000000u
#endif

#define TB_UART_BASE   0x200u
#define TB_UART_STRIDE 0x20u

#define TB_UART_DATA   0x00u
#define TB_UART_STATUS 0x04u
#define TB_UART_CTRL   0x08u

#define TB_UART_STATUS_RX_AVAIL  (1u << 0)
#define TB_UART_STATUS_TX_READY  (1u << 1)
#define TB_UART_STATUS_CONNECTED (1u << 2)
#define TB_UART_STATUS_OVERRUN   (1u << 3)

#define TB_UART_CTRL_CLR_OVERRUN (1u << 0)

static inline volatile uint32_t *tb_uart_reg(unsigned uart_idx, unsigned reg_off) {
	return (volatile uint32_t *)(TB_IO_BASE + TB_UART_BASE + (uart_idx * TB_UART_STRIDE) + reg_off);
}

static inline uint32_t tb_uart_status(unsigned uart_idx) {
	return *tb_uart_reg(uart_idx, TB_UART_STATUS);
}

static inline bool tb_uart_connected(unsigned uart_idx) {
	return (tb_uart_status(uart_idx) & TB_UART_STATUS_CONNECTED) != 0;
}

static inline bool tb_uart_can_read(unsigned uart_idx) {
	return (tb_uart_status(uart_idx) & TB_UART_STATUS_RX_AVAIL) != 0;
}

static inline int tb_uart_try_read(unsigned uart_idx) {
	if (!tb_uart_can_read(uart_idx))
		return -1;
	return (int)(*tb_uart_reg(uart_idx, TB_UART_DATA) & 0xffu);
}

static inline void tb_uart_write(unsigned uart_idx, uint8_t byte) {
	*tb_uart_reg(uart_idx, TB_UART_DATA) = (uint32_t)byte;
}

static inline void tb_uart_clear_overrun(unsigned uart_idx) {
	*tb_uart_reg(uart_idx, TB_UART_CTRL) = TB_UART_CTRL_CLR_OVERRUN;
}

#endif

