/**
 * platform.h  —  RISC-V bare-metal platform definitions
 *
 * Target: QEMU "virt" machine  (qemu-system-riscv32 -machine virt)
 *
 *   DRAM       0x8000_0000  (default 128 MB)
 *   CLINT      0x0200_0000
 *   PLIC       0x0C00_0000
 *   UART0      0x1000_0000  (NS16550A)
 *
 * Krerk Piromsopa, Ph.D.
 * Department of Computer Engineering, Chulalongkorn University
 * Computer Architecture: Design and Analysis (2026 edition)
 */

#ifndef PLATFORM_H
#define PLATFORM_H

#include <stdint.h>

/* ------------------------------------------------------------------ */
/*  UART0  (NS16550A compatible)                                        */
/*  Each register is one byte wide; do NOT use 32-bit accesses.        */
/* ------------------------------------------------------------------ */
#define UART0_BASE   0x10000000UL

/* Register offsets (byte addresses) */
#define UART_RBR     0   /* Receive Buffer Register    (read,  DLAB=0) */
#define UART_THR     0   /* Transmit Holding Register  (write, DLAB=0) */
#define UART_IER     1   /* Interrupt Enable Register                  */
#define UART_IIR     2   /* Interrupt Identification   (read)          */
#define UART_FCR     2   /* FIFO Control Register      (write)         */
#define UART_LCR     3   /* Line Control Register                      */
#define UART_MCR     4   /* Modem Control Register                     */
#define UART_LSR     5   /* Line Status Register                       */
#define UART_MSR     6   /* Modem Status Register                      */

/* LSR bits */
#define UART_LSR_DR   (1 << 0)  /* Data Ready           (RX byte available) */
#define UART_LSR_OE   (1 << 1)  /* Overrun Error                            */
#define UART_LSR_PE   (1 << 2)  /* Parity Error                             */
#define UART_LSR_FE   (1 << 3)  /* Framing Error                            */
#define UART_LSR_THRE (1 << 5)  /* TX Holding Register Empty (TX ready)     */
#define UART_LSR_TEMT (1 << 6)  /* Transmitter Empty                        */

/* IER bits */
#define UART_IER_ERBFI (1 << 0) /* Enable Received Data Available Interrupt */
#define UART_IER_ETBEI (1 << 1) /* Enable Transmit Holding Reg Empty Intr   */

/* LCR bits / values */
#define UART_LCR_8N1  0x03      /* 8 data bits, no parity, 1 stop bit       */
#define UART_LCR_DLAB (1 << 7)  /* Divisor Latch Access Bit                 */

/* FCR bits */
#define UART_FCR_ENABLE  (1 << 0) /* Enable FIFO                            */
#define UART_FCR_RXRST   (1 << 1) /* Reset RX FIFO                          */
#define UART_FCR_TXRST   (1 << 2) /* Reset TX FIFO                          */

/* Helper macro: access a UART register as a volatile byte */
#define UART_REG(base, off) \
    (*((volatile uint8_t *)((base) + (off))))

/* ------------------------------------------------------------------ */
/*  PLIC  (Platform-Level Interrupt Controller)                        */
/*  QEMU virt machine layout (RV32, Hart 0, Machine context = 0)       */
/* ------------------------------------------------------------------ */
#define PLIC_BASE            0x0C000000UL

/* Source priority:  PLIC_BASE + source*4  (sources 1..1023) */
#define PLIC_PRIORITY(src)   (*(volatile uint32_t *)(PLIC_BASE + (src)*4))

/* Interrupt pending bits: PLIC_BASE + 0x1000  (32 sources per word) */
#define PLIC_PENDING0        (*(volatile uint32_t *)(PLIC_BASE + 0x1000))

/* Enable bits for context 0 (machine-mode hart 0): PLIC_BASE + 0x2000 */
#define PLIC_ENABLE0         (*(volatile uint32_t *)(PLIC_BASE + 0x2000))

/* Context 0 registers: threshold and claim/complete */
#define PLIC_CTX0_BASE       (PLIC_BASE + 0x200000UL)
#define PLIC_THRESHOLD       (*(volatile uint32_t *)(PLIC_CTX0_BASE + 0x00))
#define PLIC_CLAIM_COMPLETE  (*(volatile uint32_t *)(PLIC_CTX0_BASE + 0x04))

/* UART0 PLIC interrupt source number on QEMU virt */
#define PLIC_SRC_UART0       10

/* ------------------------------------------------------------------ */
/*  CLINT  (Core-Local Interruptor)                                    */
/* ------------------------------------------------------------------ */
#define CLINT_BASE           0x02000000UL
#define CLINT_MSIP           (*(volatile uint32_t *)(CLINT_BASE + 0x0000))
#define CLINT_MTIMECMP_LO    (*(volatile uint32_t *)(CLINT_BASE + 0x4000))
#define CLINT_MTIMECMP_HI    (*(volatile uint32_t *)(CLINT_BASE + 0x4004))
#define CLINT_MTIME_LO       (*(volatile uint32_t *)(CLINT_BASE + 0xBFF8))
#define CLINT_MTIME_HI       (*(volatile uint32_t *)(CLINT_BASE + 0xBFFC))

/* ------------------------------------------------------------------ */
/*  CSR helpers                                                        */
/* ------------------------------------------------------------------ */
#define CSR_READ(csr)        ({ uint32_t _v; __asm__ volatile("csrr %0," #csr : "=r"(_v)); _v; })
#define CSR_WRITE(csr, val)  __asm__ volatile("csrw " #csr ", %0" :: "r"((uint32_t)(val)))
#define CSR_SET(csr, mask)   __asm__ volatile("csrs " #csr ", %0" :: "r"((uint32_t)(mask)))
#define CSR_CLEAR(csr, mask) __asm__ volatile("csrc " #csr ", %0" :: "r"((uint32_t)(mask)))

/* mstatus bits */
#define MSTATUS_MIE  (1 << 3)

/* mie / mip bits */
#define MIE_MSIE     (1 << 3)   /* Machine Software Interrupt Enable  */
#define MIE_MTIE     (1 << 7)   /* Machine Timer    Interrupt Enable  */
#define MIE_MEIE     (1 << 11)  /* Machine External Interrupt Enable  */

/* mcause values */
#define MCAUSE_INT_BIT   (1u << 31)
#define MCAUSE_M_EXT     11   /* Machine external interrupt */

#endif /* PLATFORM_H */
