Skip to content

File port_ch32v307.c

File List > ch32v307 > port_ch32v307.c

Go to the documentation of this file

#if defined(CH32V307xx) || defined(CH32V307)

#include "syntropic/common/syn_compiler.h"
#include "syntropic/common/syn_defs.h"
#include "syntropic/port/syn_port_adc.h"
#include "syntropic/port/syn_port_can.h"
#include "syntropic/port/syn_port_exti.h"
#include "syntropic/port/syn_port_flash.h"
#include "syntropic/port/syn_port_gpio.h"
#include "syntropic/port/syn_port_i2c.h"
#include "syntropic/port/syn_port_spi.h"
#include "syntropic/port/syn_port_system.h"
#include "syntropic/port/syn_port_uart.h"
#include "syntropic/system/syn_sleep.h"

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

/* ═══════════════════════════════════════════════════════════════════════════
 *  Register Map & Definitions
 * ═══════════════════════════════════════════════════════════════════════════ */

#define PERIPH_BASE 0x40000000UL
#define APB1_BASE PERIPH_BASE
#define APB2_BASE (PERIPH_BASE + 0x10000UL)
#define AHB1_BASE (PERIPH_BASE + 0x20000UL)

/* ── RCC ────────────────────────────────────────────────────────────────── */

#define RCC_BASE (AHB1_BASE + 0x1000UL)
#define RCC_CTLR (*(volatile uint32_t *)(RCC_BASE + 0x00))
#define RCC_CFGR0 (*(volatile uint32_t *)(RCC_BASE + 0x04))
#define RCC_AHBCNTR (*(volatile uint32_t *)(RCC_BASE + 0x14))
#define RCC_APB2PCENR (*(volatile uint32_t *)(RCC_BASE + 0x18))
#define RCC_APB1PCENR (*(volatile uint32_t *)(RCC_BASE + 0x1C))

#define RCC_IOPAEN (1U << 2)
#define RCC_IOPBEN (1U << 3)
#define RCC_IOPCEN (1U << 4)
#define RCC_IOPDEN (1U << 5)
#define RCC_IOPEEN (1U << 6)

#define RCC_USART1EN (1U << 14)
#define RCC_SPI1EN (1U << 12)
#define RCC_I2C1EN (1U << 21)
#define RCC_CAN1EN (1U << 25)
#define RCC_ADC1EN (1U << 9)

/* ── SysTick (STK) ──────────────────────────────────────────────────────── */

#define STK_CTRL (*(volatile uint32_t *)(0xE000E010UL + 0x00))
#define STK_SR (*(volatile uint32_t *)(0xE000E010UL + 0x04))
#define STK_CNTL (*(volatile uint32_t *)(0xE000E010UL + 0x08))
#define STK_CNTH (*(volatile uint32_t *)(0xE000E010UL + 0x0C))

/* ── GPIO ───────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t CFGLR;
    volatile uint32_t CFGHR;
    volatile uint32_t INDR;
    volatile uint32_t OUTDR;
    volatile uint32_t BSHR;
    volatile uint32_t BCR;
    volatile uint32_t LCKR;
} GPIO_TypeDef;

#define GPIOA ((GPIO_TypeDef *)(APB2_BASE + 0x0800))
#define GPIOB ((GPIO_TypeDef *)(APB2_BASE + 0x0C00))
#define GPIOC ((GPIO_TypeDef *)(APB2_BASE + 0x1000))
#define GPIOD ((GPIO_TypeDef *)(APB2_BASE + 0x1400))
#define GPIOE ((GPIO_TypeDef *)(APB2_BASE + 0x1800))

static GPIO_TypeDef *const gpio_ports[] = {GPIOA, GPIOB, GPIOC, GPIOD, GPIOE};
#define NUM_GPIO_PORTS (sizeof(gpio_ports) / sizeof(gpio_ports[0]))

/* ── USART ──────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t STATR;
    volatile uint32_t DATAR;
    volatile uint32_t BRR;
    volatile uint32_t CTLR1;
    volatile uint32_t CTLR2;
    volatile uint32_t CTLR3;
} USART_TypeDef;

#define USART1 ((USART_TypeDef *)(APB2_BASE + 0x3800))

#define USART_STATR_TXE (1U << 7)
#define USART_STATR_RXNE (1U << 5)
#define USART_CTLR1_UE (1U << 13)
#define USART_CTLR1_TE (1U << 3)
#define USART_CTLR1_RE (1U << 2)

/* ── Flash Controller ───────────────────────────────────────────────────── */

#define FLASH_BASE (AHB1_BASE + 0x2000UL)
#define FLASH_KEYR (*(volatile uint32_t *)(FLASH_BASE + 0x04))
#define FLASH_STATR (*(volatile uint32_t *)(FLASH_BASE + 0x0C))
#define FLASH_CTLR (*(volatile uint32_t *)(FLASH_BASE + 0x10))
#define FLASH_ADDR (*(volatile uint32_t *)(FLASH_BASE + 0x14))

#define FLASH_KEY1 0x45670123UL
#define FLASH_KEY2 0xCDEF89ABUL

#define FLASH_CTLR_PER (1U << 1)
#define FLASH_CTLR_STRT (1U << 6)
#define FLASH_CTLR_LOCK (1U << 7)
#define FLASH_STATR_BSY (1U << 0)

/* ── SPI ────────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t CTLR1;
    volatile uint32_t CTLR2;
    volatile uint32_t STATR;
    volatile uint32_t DATAR;
} SPI_TypeDef;

#define SPI1 ((SPI_TypeDef *)(APB2_BASE + 0x3000))

#define SPI_CTLR1_SPE (1U << 6)
#define SPI_CTLR1_MSTR (1U << 2)
#define SPI_STATR_TXE (1U << 1)
#define SPI_STATR_RXNE (1U << 0)
#define SPI_STATR_BSY (1U << 7)

/* ── I2C ────────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t CTLR1;
    volatile uint32_t CTLR2;
    volatile uint32_t OADDR1;
    volatile uint32_t OADDR2;
    volatile uint32_t DATAR;
    volatile uint32_t STAR1;
    volatile uint32_t STAR2;
    volatile uint32_t CKCFGR;
} I2C_TypeDef;

#define I2C1 ((I2C_TypeDef *)(APB1_BASE + 0x5400))

#define I2C_CTLR1_PE (1U << 0)
#define I2C_CTLR1_START(1U << 8)
#define I2C_CTLR1_STOP (1U << 9)
#define I2C_CTLR1_ACK (1U << 10)
#define I2C_STAR1_SB (1U << 0)
#define I2C_STAR1_ADDR (1U << 1)
#define I2C_STAR1_TXE (1U << 7)
#define I2C_STAR1_RXNE (1U << 6)

/* ── CAN (bxCAN) ────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t MCR;
    volatile uint32_t MSR;
    volatile uint32_t TSR;
    volatile uint32_t RF0R;
    volatile uint32_t RF1R;
    volatile uint32_t IER;
    volatile uint32_t ESR;
    volatile uint32_t BTR;
} CAN_TypeDef;

#define CAN1 ((CAN_TypeDef *)(APB1_BASE + 0x6400))

#define CAN_MCR_INRQ (1U << 0)
#define CAN_MSR_INAK (1U << 0)
#define CAN_TSR_TME0 (1U << 26)
#define CAN_RF0R_FMP0 (3U << 0)

/* ── ADC ────────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t STATR;
    volatile uint32_t CTLR1;
    volatile uint32_t CTLR2;
    volatile uint32_t SAMPTR1;
    volatile uint32_t SAMPTR2;
    volatile uint32_t IOFR1;
    volatile uint32_t IOFR2;
    volatile uint32_t IOFR3;
    volatile uint32_t IOFR4;
    volatile uint32_t RSQR1;
    volatile uint32_t RSQR2;
    volatile uint32_t RSQR3;
    volatile uint32_t ISQR;
    volatile uint32_t IDATAR1;
    volatile uint32_t IDATAR2;
    volatile uint32_t IDATAR3;
    volatile uint32_t IDATAR4;
    volatile uint32_t RDATAR;
} ADC_TypeDef;

#define ADC1 ((ADC_TypeDef *)(APB2_BASE + 0x2400))

#define ADC_CTLR2_ADON (1U << 0)
#define ADC_CTLR2_SWSTART (1U << 22)
#define ADC_STATR_EOC (1U << 1)

/* ── EXTI ───────────────────────────────────────────────────────────────── */

typedef struct {
    volatile uint32_t INTENR;
    volatile uint32_t EVENR;
    volatile uint32_t RTENR;
    volatile uint32_t FTENR;
    volatile uint32_t SWIEVR;
    volatile uint32_t INTFR;
} EXTI_TypeDef;

#define EXTI ((EXTI_TypeDef *)(APB2_BASE + 0x0400))

/* ═══════════════════════════════════════════════════════════════════════════
 *  Global State
 * ═══════════════════════════════════════════════════════════════════════════ */

static volatile uint32_t s_tick_ms = 0U;
static volatile uint32_t s_critical_nesting = 0U;

#if defined(__riscv)
#define SYN_ISR_ATTR __attribute__((interrupt("WCH-Interrupt-fast")))
#else
#define SYN_ISR_ATTR
#endif

/* STK SysTick Interrupt Handler — executed via PFIC hardware HPE engine */
SYN_ISR_ATTR void SysTick_Handler(void)
{
    STK_SR = 0;
    s_tick_ms++;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  System & Clock Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

void syn_port_enter_critical(void)
{
    __asm__ volatile("csrci mstatus, 8");
    s_critical_nesting++;
}

void syn_port_exit_critical(void)
{
    if (s_critical_nesting > 0) {
        s_critical_nesting--;
        if (s_critical_nesting == 0) {
            __asm__ volatile("csrsi mstatus, 8");
        }
    }
}

uint32_t syn_port_get_tick_ms(void)
{
    return s_tick_ms;
}

uint32_t syn_port_get_tick_us(void)
{
    return s_tick_ms * 1000U;
}

void syn_port_delay_ms(uint32_t ms)
{
    uint32_t start = syn_port_get_tick_ms();
    while ((syn_port_get_tick_ms() - start) < ms) {
        __asm__ volatile("nop");
    }
}

void syn_port_nvic_set_priority(uint8_t irq_num, uint8_t preempt_prio, uint8_t sub_prio)
{
    /* QingKe V4F PFIC priority register base: 0xE000E000 + 0x400 + irq_num */
    volatile uint8_t *iprior = (volatile uint8_t *)0xE000E400UL;
    iprior[irq_num] = (uint8_t)((preempt_prio << 6) | (sub_prio << 4));
}

void syn_port_nvic_enable_irq(uint8_t irq_num)
{
    /* QingKe V4F PFIC enable register: 0xE000E100 */
    volatile uint32_t *ienr = (volatile uint32_t *)0xE000E100UL;
    ienr[irq_num >> 5] |= (1U << (irq_num & 0x1FU));
}

SYN_NORETURN void syn_port_system_reset(void)
{
    *(volatile uint32_t *)0xE000ED0C = 0x05FA0004;
    while (1) {
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  GPIO Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_gpio_init(SYN_GPIO_Pin pin, SYN_GPIO_Mode mode)
{
    uint32_t port_idx = pin >> 4;
    uint32_t pin_idx = pin & 0x0FU;

    if (port_idx >= NUM_GPIO_PORTS) {
        return SYN_INVALID_PARAM;
    }

    RCC_APB2PCENR |= (1U << (port_idx + 2));

    GPIO_TypeDef *gpio = gpio_ports[port_idx];

    uint32_t config_val = 0U;
    if (mode == SYN_GPIO_MODE_OUTPUT_PP) {
        config_val = 0x3U;
    } else if (mode == SYN_GPIO_MODE_INPUT_PULLUP || mode == SYN_GPIO_MODE_INPUT_PULLDOWN) {
        config_val = 0x8U;
    } else {
        config_val = 0x4U;
    }

    if (pin_idx < 8) {
        uint32_t shift = pin_idx * 4U;
        gpio->CFGLR = (gpio->CFGLR & ~(0xFU << shift)) | (config_val << shift);
    } else {
        uint32_t shift = (pin_idx - 8U) * 4U;
        gpio->CFGHR = (gpio->CFGHR & ~(0xFU << shift)) | (config_val << shift);
    }

    return SYN_OK;
}

SYN_Status syn_port_gpio_deinit(SYN_GPIO_Pin pin)
{
    return syn_port_gpio_init(pin, SYN_GPIO_MODE_INPUT_FLOAT);
}

SYN_Status syn_port_gpio_write(SYN_GPIO_Pin pin, SYN_GPIO_State state)
{
    uint32_t port_idx = pin >> 4;
    uint32_t pin_idx = pin & 0x0FU;

    if (port_idx >= NUM_GPIO_PORTS) {
        return SYN_INVALID_PARAM;
    }

    GPIO_TypeDef *gpio = gpio_ports[port_idx];
    if (state == SYN_GPIO_HIGH) {
        gpio->BSHR = (1U << pin_idx);
    } else {
        gpio->BCR = (1U << pin_idx);
    }

    return SYN_OK;
}

SYN_GPIO_State syn_port_gpio_read(SYN_GPIO_Pin pin)
{
    uint32_t port_idx = pin >> 4;
    uint32_t pin_idx = pin & 0x0FU;

    if (port_idx >= NUM_GPIO_PORTS) {
        return SYN_GPIO_LOW;
    }

    GPIO_TypeDef *gpio = gpio_ports[port_idx];
    return (gpio->INDR & (1U << pin_idx)) ? SYN_GPIO_HIGH : SYN_GPIO_LOW;
}

SYN_Status syn_port_gpio_toggle(SYN_GPIO_Pin pin)
{
    SYN_GPIO_State curr = syn_port_gpio_read(pin);
    return syn_port_gpio_write(pin, (curr == SYN_GPIO_HIGH) ? SYN_GPIO_LOW : SYN_GPIO_HIGH);
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  UART Implementation (USART1)
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_uart_init(SYN_UARTInstance instance, uint32_t baudrate)
{
    if (instance != 0) {
        return SYN_NOT_SUPPORTED;
    }

    RCC_APB2PCENR |= RCC_USART1EN;

#ifndef SYN_PORT_SYSCLK_HZ
#define SYN_PORT_SYSCLK_HZ 144000000UL
#endif

    uint32_t pclk = SYN_PORT_SYSCLK_HZ;
    USART1->BRR = (pclk + baudrate / 2U) / baudrate;
    USART1->CTLR1 = USART_CTLR1_UE | USART_CTLR1_TE | USART_CTLR1_RE;

    return SYN_OK;
}

SYN_Status syn_port_uart_deinit(SYN_UARTInstance instance)
{
    if (instance != 0)
        return SYN_NOT_SUPPORTED;
    USART1->CTLR1 = 0;
    return SYN_OK;
}

SYN_Status syn_port_uart_transmit_byte(SYN_UARTInstance instance, uint8_t byte)
{
    if (instance != 0)
        return SYN_NOT_SUPPORTED;

    while (!(USART1->STATR & USART_STATR_TXE)) {
    }
    USART1->DATAR = byte;

    return SYN_OK;
}

SYN_Status syn_port_uart_transmit(SYN_UARTInstance instance, const uint8_t *data, size_t len,
                                  uint32_t timeout_ms)
{
    (void)timeout_ms;
    if (data == NULL)
        return SYN_INVALID_PARAM;

    for (size_t i = 0; i < len; i++) {
        syn_port_uart_transmit_byte(instance, data[i]);
    }
    return SYN_OK;
}

SYN_Status syn_port_uart_receive_byte(SYN_UARTInstance instance, uint8_t *byte, uint32_t timeout_ms)
{
    (void)timeout_ms;
    if (instance != 0 || byte == NULL)
        return SYN_INVALID_PARAM;

    if (USART1->STATR & USART_STATR_RXNE) {
        *byte = (uint8_t)USART1->DATAR;
        return SYN_OK;
    }
    return SYN_TIMEOUT;
}

SYN_Status syn_port_uart_receive(SYN_UARTInstance instance, uint8_t *data, size_t len,
                                 size_t *received, uint32_t timeout_ms)
{
    (void)timeout_ms;
    if (data == NULL || received == NULL)
        return SYN_INVALID_PARAM;

    size_t count = 0;
    for (size_t i = 0; i < len; i++) {
        if (syn_port_uart_receive_byte(instance, &data[i], 0) == SYN_OK) {
            count++;
        } else {
            break;
        }
    }
    *received = count;
    return SYN_OK;
}

int _write(int file, char *ptr, int len)
{
    (void)file;
    syn_port_uart_transmit(0, (const uint8_t *)ptr, (size_t)len, 100U);
    return len;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  Flash Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_flash_read(uint32_t addr, void *buf, size_t len)
{
    if (buf == NULL)
        return SYN_INVALID_PARAM;
    memcpy(buf, (const void *)addr, len);
    return SYN_OK;
}

SYN_Status syn_port_flash_erase(uint32_t addr)
{
    if (FLASH_CTLR & FLASH_CTLR_LOCK) {
        FLASH_KEYR = FLASH_KEY1;
        FLASH_KEYR = FLASH_KEY2;
    }

    while (FLASH_STATR & FLASH_STATR_BSY) {
    }

    FLASH_CTLR |= FLASH_CTLR_PER;
    FLASH_ADDR = addr;
    FLASH_CTLR |= FLASH_CTLR_STRT;

    while (FLASH_STATR & FLASH_STATR_BSY) {
    }

    FLASH_CTLR &= ~FLASH_CTLR_PER;
    FLASH_CTLR |= FLASH_CTLR_LOCK;

    return SYN_OK;
}

SYN_Status syn_port_flash_write(uint32_t addr, const void *buf, size_t len)
{
    if (buf == NULL)
        return SYN_INVALID_PARAM;

    if (FLASH_CTLR & FLASH_CTLR_LOCK) {
        FLASH_KEYR = FLASH_KEY1;
        FLASH_KEYR = FLASH_KEY2;
    }

    const uint32_t *src = (const uint32_t *)buf;
    size_t words = len / 4U;

    for (size_t i = 0; i < words; i++) {
        while (FLASH_STATR & FLASH_STATR_BSY) {
        }
        *(volatile uint32_t *)(addr + i * 4U) = src[i];
    }

    FLASH_CTLR |= FLASH_CTLR_LOCK;
    return SYN_OK;
}

uint32_t syn_port_flash_sector_size(uint32_t addr)
{
    (void)addr;
    return 4U * 1024U;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  SPI Implementation (SPI1)
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_spi_init(uint8_t spi_id, uint32_t baudrate_hz, uint8_t mode, uint8_t role)
{
    (void)baudrate_hz;
    (void)mode;
    (void)role;
    if (spi_id != 0)
        return SYN_NOT_SUPPORTED;

    RCC_APB2PCENR |= RCC_SPI1EN | RCC_IOPAEN;
    SPI1->CTLR1 = SPI_CTLR1_MSTR | SPI_CTLR1_SPE;
    return SYN_OK;
}

SYN_Status syn_port_spi_deinit(uint8_t spi_id)
{
    if (spi_id != 0)
        return SYN_NOT_SUPPORTED;
    SPI1->CTLR1 = 0;
    return SYN_OK;
}

SYN_Status syn_port_spi_transfer(uint8_t spi_id, const uint8_t *tx, uint8_t *rx, size_t len)
{
    if (spi_id != 0)
        return SYN_NOT_SUPPORTED;

    for (size_t i = 0; i < len; i++) {
        uint8_t send_val = tx ? tx[i] : 0xFFU;

        while (!(SPI1->STATR & SPI_STATR_TXE)) {
        }
        SPI1->DATAR = send_val;

        while (!(SPI1->STATR & SPI_STATR_RXNE)) {
        }
        uint8_t recv_val = (uint8_t)SPI1->DATAR;
        if (rx) {
            rx[i] = recv_val;
        }
    }
    return SYN_OK;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  I2C Implementation (I2C1)
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_i2c_init(uint8_t i2c_id, uint32_t clock_speed_hz, uint8_t role,
                             uint16_t own_addr)
{
    (void)clock_speed_hz;
    (void)role;
    (void)own_addr;
    if (i2c_id != 0)
        return SYN_NOT_SUPPORTED;

    RCC_APB1PCENR |= RCC_I2C1EN;
    I2C1->CTLR1 = I2C_CTLR1_PE;
    return SYN_OK;
}

SYN_Status syn_port_i2c_deinit(uint8_t i2c_id)
{
    if (i2c_id != 0)
        return SYN_NOT_SUPPORTED;
    I2C1->CTLR1 = 0;
    return SYN_OK;
}

SYN_Status syn_port_i2c_transfer(uint8_t i2c_id, uint16_t addr, const uint8_t *tx, size_t tx_len,
                                 uint8_t *rx, size_t rx_len)
{
    if (i2c_id != 0)
        return SYN_NOT_SUPPORTED;

    /* Generate START condition */
    I2C1->CTLR1 |= I2C_CTLR1_START;
    while (!(I2C1->STAR1 & I2C_STAR1_SB)) {
    }

    if (tx != NULL && tx_len > 0) {
        /* Send 7-bit slave address + Write bit (0) */
        I2C1->DATAR = (uint8_t)(addr << 1);
        while (!(I2C1->STAR1 & I2C_STAR1_ADDR)) {
        }
        (void)I2C1->STAR2;

        for (size_t i = 0; i < tx_len; i++) {
            while (!(I2C1->STAR1 & I2C_STAR1_TXE)) {
            }
            I2C1->DATAR = tx[i];
        }
    }

    if (rx != NULL && rx_len > 0) {
        /* Generate REPEATED START condition for read */
        I2C1->CTLR1 |= I2C_CTLR1_START;
        while (!(I2C1->STAR1 & I2C_STAR1_SB)) {
        }

        /* Send 7-bit slave address + Read bit (1) */
        I2C1->DATAR = (uint8_t)((addr << 1) | 1U);
        while (!(I2C1->STAR1 & I2C_STAR1_ADDR)) {
        }
        (void)I2C1->STAR2;

        for (size_t i = 0; i < rx_len; i++) {
            if (i == rx_len - 1) {
                I2C1->CTLR1 &= ~I2C_CTLR1_ACK;
            } else {
                I2C1->CTLR1 |= I2C_CTLR1_ACK;
            }
            while (!(I2C1->STAR1 & I2C_STAR1_RXNE)) {
            }
            rx[i] = (uint8_t)I2C1->DATAR;
        }
    }

    /* Generate STOP condition */
    I2C1->CTLR1 |= I2C_CTLR1_STOP;
    return SYN_OK;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  CAN Implementation (CAN1 bxCAN)
 * ═══════════════════════════════════════════════════════════════════════════ */

/* CAN1 bxCAN-compatible controller initialization. Leaves initialization mode upon exit. */
bool syn_port_can_init(uint8_t port, uint32_t bitrate)
{
    (void)bitrate;
    if (port != 0)
        return false;

    RCC_APB1PCENR |= RCC_CAN1EN;
    CAN1->MCR &= ~CAN_MCR_INRQ;
    return true;
}

/* Transmit frame via CAN1 mailbox 0. */
bool syn_port_can_send(uint8_t port, uint32_t id, bool extended, const uint8_t *data, uint8_t dlc)
{
    if (port != 0 || dlc > 8)
        return false;

    if (!(CAN1->TSR & CAN_TSR_TME0)) {
        return false;
    }

    typedef struct {
        volatile uint32_t TIR;
        volatile uint32_t TDTR;
        volatile uint32_t TDLR;
        volatile uint32_t TDHR;
    } CAN_TxMailBox_TypeDef;

    CAN_TxMailBox_TypeDef *mb = (CAN_TxMailBox_TypeDef *)(APB1_BASE + 0x6400 + 0x180);

    uint32_t tir = 0;
    if (extended) {
        tir = (id << 3) | (1U << 2);
    } else {
        tir = (id << 21);
    }
    tir |= (1U << 0);

    mb->TDTR = (dlc & 0x0FU);
    if (data != NULL) {
        uint32_t tdlr = 0, tdhr = 0;
        for (uint8_t i = 0; i < dlc && i < 4; i++) {
            tdlr |= ((uint32_t)data[i] << (i * 8));
        }
        for (uint8_t i = 4; i < dlc && i < 8; i++) {
            tdhr |= ((uint32_t)data[i] << ((i - 4) * 8));
        }
        mb->TDLR = tdlr;
        mb->TDHR = tdhr;
    }
    mb->TIR = tir;
    return true;
}

/* Non-blocking read from CAN1 FIFO0 buffer. */
bool syn_port_can_receive(uint8_t port, uint32_t *id, bool *extended, uint8_t *data, uint8_t *dlc)
{
    if (port != 0 || id == NULL || extended == NULL || data == NULL || dlc == NULL)
        return false;

    if ((CAN1->RF0R & CAN_RF0R_FMP0) == 0) {
        return false;
    }

    typedef struct {
        volatile uint32_t RIR;
        volatile uint32_t RDTR;
        volatile uint32_t RDLR;
        volatile uint32_t RDHR;
    } CAN_FIFOMailBox_TypeDef;

    CAN_FIFOMailBox_TypeDef *fifo = (CAN_FIFOMailBox_TypeDef *)(APB1_BASE + 0x6400 + 0x1B0);

    uint32_t rir = fifo->RIR;
    if (rir & (1U << 2)) {
        *extended = true;
        *id = (rir >> 3);
    } else {
        *extended = false;
        *id = (rir >> 21);
    }

    uint8_t len = (uint8_t)(fifo->RDTR & 0x0FU);
    *dlc = len;

    uint32_t rdlr = fifo->RDLR;
    uint32_t rdhr = fifo->RDHR;
    for (uint8_t i = 0; i < len && i < 4; i++) {
        data[i] = (uint8_t)(rdlr >> (i * 8));
    }
    for (uint8_t i = 4; i < len && i < 8; i++) {
        data[i] = (uint8_t)(rdhr >> ((i - 4) * 8));
    }

    CAN1->RF0R |= (1U << 5);
    return true;
}

/* Configure hardware 32-bit acceptance filter bank. */
void syn_port_can_set_filter(uint8_t port, uint32_t id, uint32_t mask)
{
    if (port != 0)
        return;

    typedef struct {
        volatile uint32_t F1R;
        volatile uint32_t F2R;
    } CAN_FilterRegister_TypeDef;

    volatile uint32_t *FMR = (volatile uint32_t *)(APB1_BASE + 0x6400 + 0x200);
    volatile uint32_t *FA1R = (volatile uint32_t *)(APB1_BASE + 0x6400 + 0x21C);
    CAN_FilterRegister_TypeDef *FR = (CAN_FilterRegister_TypeDef *)(APB1_BASE + 0x6400 + 0x240);

    *FMR |= (1U << 0);
    *FA1R &= ~(1U << 0);

    FR[0].F1R = (id << 21);
    FR[0].F2R = (mask << 21);

    *FA1R |= (1U << 0);
    *FMR &= ~(1U << 0);
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  ADC Implementation (ADC1)
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_adc_init(uint8_t adc_id, uint32_t channel_mask)
{
    (void)channel_mask;
    if (adc_id != 0)
        return SYN_NOT_SUPPORTED;

    RCC_APB2PCENR |= RCC_ADC1EN;
    ADC1->CTLR2 |= ADC_CTLR2_ADON;
    return SYN_OK;
}

SYN_Status syn_port_adc_deinit(uint8_t adc_id)
{
    if (adc_id != 0)
        return SYN_NOT_SUPPORTED;
    ADC1->CTLR2 &= ~ADC_CTLR2_ADON;
    return SYN_OK;
}

/* Single-shot polled conversion on ADC1 channel. */
uint16_t syn_port_adc_read_channel(uint8_t adc_id, uint8_t channel)
{
    (void)channel;
    if (adc_id != 0)
        return 0U;

    ADC1->CTLR2 |= ADC_CTLR2_SWSTART;
    while (!(ADC1->STATR & ADC_STATR_EOC)) {
    }
    return (uint16_t)ADC1->RDATAR;
}

/* Trigger multi-channel background scan stream over DMA1 Channel 1. */
SYN_Status syn_port_adc_start_dma_scan(uint8_t adc_id, uint16_t *dest, size_t num_channels)
{
    if (adc_id != 0 || dest == NULL || num_channels == 0) {
        return SYN_INVALID_PARAM;
    }

    /* Enable DMA1 clock */
    RCC_AHBCNTR |= (1U << 0);

    /* Configure DMA1 Channel 1 (ADC1 peripheral to memory transfer) */
    SYN_PortDmaTransfer xfer = {
        .channel_id = 0,
        .dir = SYN_DMA_DIR_PERIPH_TO_MEM,
        .data_size = SYN_DMA_SIZE_16BIT,
        .src_inc = false,
        .dst_inc = true,
        .src = (const void *)&ADC1->RDATAR,
        .dst = dest,
        .count = num_channels,
    };

    SYN_Status st = syn_port_dma_start(&xfer);
    if (st == SYN_OK) {
        ADC1->CTLR2 |= (1U << 8); /* Enable ADC DMA request bit */
    }
    return st;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  EXTI Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

SYN_Status syn_port_exti_configure(SYN_GPIO_Pin pin, SYN_EXTI_Edge edge)
{
    uint32_t pin_idx = pin & 0x0FU;

    if (edge == SYN_EXTI_RISING || edge == SYN_EXTI_BOTH) {
        EXTI->RTENR |= (1U << pin_idx);
    }
    if (edge == SYN_EXTI_FALLING || edge == SYN_EXTI_BOTH) {
        EXTI->FTENR |= (1U << pin_idx);
    }
    return SYN_OK;
}

void syn_port_exti_enable(SYN_GPIO_Pin pin)
{
    uint32_t pin_idx = pin & 0x0FU;
    EXTI->INTENR |= (1U << pin_idx);
}

void syn_port_exti_disable(SYN_GPIO_Pin pin)
{
    uint32_t pin_idx = pin & 0x0FU;
    EXTI->INTENR &= ~(1U << pin_idx);
}

void syn_port_exti_clear_pending(SYN_GPIO_Pin pin)
{
    uint32_t pin_idx = pin & 0x0FU;
    EXTI->INTFR = (1U << pin_idx);
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  DMA Implementation (DMA1 / DMA2)
 * ═══════════════════════════════════════════════════════════════════════════ */

#include "syntropic/port/syn_port_dma.h"

typedef struct {
    volatile uint32_t CFGR;
    volatile uint32_t CNTR;
    volatile uint32_t PADDR;
    volatile uint32_t MADDR;
} DMA_Channel_TypeDef;

typedef struct {
    volatile uint32_t INTFR;
    volatile uint32_t INTFCR;
    DMA_Channel_TypeDef CH[7];
} DMA_TypeDef;

#define DMA1 ((DMA_TypeDef *)(AHB1_BASE + 0x0000))

SYN_Status syn_port_dma_start(const SYN_PortDmaTransfer *xfer)
{
    if (xfer == NULL || xfer->channel_id >= 7U) {
        return SYN_INVALID_PARAM;
    }

    DMA_Channel_TypeDef *ch = &DMA1->CH[xfer->channel_id];
    ch->CFGR &= ~1U;

    ch->PADDR = (uint32_t)xfer->src;
    ch->MADDR = (uint32_t)xfer->dst;
    ch->CNTR = (uint32_t)xfer->count;

    uint32_t cfgr = 0U;
    if (xfer->dir == SYN_DMA_DIR_MEM_TO_MEM) {
        cfgr |= (1U << 14);
    } else if (xfer->dir == SYN_DMA_DIR_MEM_TO_PERIPH) {
        cfgr |= (1U << 4);
    }
    if (xfer->src_inc)
        cfgr |= (1U << 6);
    if (xfer->dst_inc)
        cfgr |= (1U << 7);

    ch->CFGR = cfgr | 1U;
    return SYN_OK;
}

SYN_Status syn_port_dma_stop(uint8_t channel_id)
{
    if (channel_id >= 7U)
        return SYN_INVALID_PARAM;
    DMA1->CH[channel_id].CFGR &= ~1U;
    return SYN_OK;
}

bool syn_port_dma_is_busy(uint8_t channel_id)
{
    if (channel_id >= 7U)
        return false;
    return (DMA1->CH[channel_id].CFGR & 1U) != 0U;
}

uint32_t syn_port_dma_get_counter(uint8_t channel_id)
{
    if (channel_id >= 7U)
        return 0U;
    return DMA1->CH[channel_id].CNTR;
}

/* QingKe V4F RISC-V core has no L1 Data Cache; CPU and DMA access SRAM directly
 * with native hardware coherency. Cache maintenance operations are no-ops. */
void syn_port_cache_clean(const void *addr, size_t len)
{
    (void)addr;
    (void)len;
}

void syn_port_cache_invalidate(void *addr, size_t len)
{
    (void)addr;
    (void)len;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  Ethernet Implementation (10/100M MAC + DMA Descriptor Ring)
 * ═══════════════════════════════════════════════════════════════════════════ */

#include "src/port/syn_port_eth.h"

typedef struct {
    volatile uint32_t Status;
    volatile uint32_t ControlBufferSize;
    volatile uint32_t Buffer1Addr;
    volatile uint32_t Buffer2NextDescAddr;
} ETH_DMADescTypeDef;

#define ETH_DMA_BASE 0x40029000UL

typedef struct {
    volatile uint32_t BMR;
    volatile uint32_t TPDR;
    volatile uint32_t RPDR;
    volatile uint32_t RDLAR;
    volatile uint32_t TDLAR;
    volatile uint32_t SR;
    volatile uint32_t OMR;
    volatile uint32_t IER;
} ETH_DMA_TypeDef;

#define ETH_DMA ((ETH_DMA_TypeDef *)ETH_DMA_BASE)

#define ETH_DMATXDESC_OWN (1U << 31)
#define ETH_DMARXDESC_OWN (1U << 31)

#define ETH_TX_DESC_NUM 4
#define ETH_RX_DESC_NUM 4
#define ETH_MAX_PACKET_SIZE 1536

static ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_NUM] __attribute__((aligned(4)));
static ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_NUM] __attribute__((aligned(4)));
static uint8_t Rx_Buff[ETH_RX_DESC_NUM][ETH_MAX_PACKET_SIZE] __attribute__((aligned(4)));
static uint8_t Tx_Buff[ETH_TX_DESC_NUM][ETH_MAX_PACKET_SIZE] __attribute__((aligned(4)));

static uint32_t s_tx_desc_idx = 0;
static uint32_t s_rx_desc_idx = 0;

SYN_Status syn_port_eth_init(const uint8_t mac_addr[6])
{
    (void)mac_addr;

    /* Enable Ethernet MAC & PHY clock */
    RCC_AHBCNTR |= (1U << 14);

    /* Software Reset ETH DMA */
    ETH_DMA->BMR |= (1U << 0);
    while (ETH_DMA->BMR & (1U << 0)) {
    }

    /* Setup Tx DMA Descriptors */
    for (uint32_t i = 0; i < ETH_TX_DESC_NUM; i++) {
        DMATxDscrTab[i].Status = 0;
        DMATxDscrTab[i].ControlBufferSize = 0;
        DMATxDscrTab[i].Buffer1Addr = (uint32_t)&Tx_Buff[i][0];
        DMATxDscrTab[i].Buffer2NextDescAddr = (uint32_t)&DMATxDscrTab[(i + 1) % ETH_TX_DESC_NUM];
    }
    ETH_DMA->TDLAR = (uint32_t)&DMATxDscrTab[0];

    /* Setup Rx DMA Descriptors */
    for (uint32_t i = 0; i < ETH_RX_DESC_NUM; i++) {
        DMARxDscrTab[i].Status = ETH_DMARXDESC_OWN;
        DMARxDscrTab[i].ControlBufferSize = ETH_MAX_PACKET_SIZE;
        DMARxDscrTab[i].Buffer1Addr = (uint32_t)&Rx_Buff[i][0];
        DMARxDscrTab[i].Buffer2NextDescAddr = (uint32_t)&DMARxDscrTab[(i + 1) % ETH_RX_DESC_NUM];
    }
    ETH_DMA->RDLAR = (uint32_t)&DMARxDscrTab[0];

    /* Start Ethernet Transmission & Reception DMA engines */
    ETH_DMA->OMR |= (1U << 13) | (1U << 1); /* ST (Start Tx) | SR (Start Rx) */
    return SYN_OK;
}

SYN_Status syn_port_eth_tx(const void *frame, size_t len)
{
    if (frame == NULL || len == 0 || len > ETH_MAX_PACKET_SIZE) {
        return SYN_INVALID_PARAM;
    }

    ETH_DMADescTypeDef *desc = &DMATxDscrTab[s_tx_desc_idx];
    if (desc->Status & ETH_DMATXDESC_OWN) {
        return SYN_BUSY;
    }

    memcpy(Tx_Buff[s_tx_desc_idx], frame, len);
    desc->ControlBufferSize = (uint32_t)len;
    desc->Status = ETH_DMATXDESC_OWN;

    ETH_DMA->TPDR = 0; /* Transmit poll demand */
    s_tx_desc_idx = (s_tx_desc_idx + 1) % ETH_TX_DESC_NUM;
    return SYN_OK;
}

SYN_Status syn_port_eth_rx(void *buf, size_t max_len, size_t *out_len)
{
    if (buf == NULL || out_len == NULL) {
        return SYN_INVALID_PARAM;
    }

    ETH_DMADescTypeDef *desc = &DMARxDscrTab[s_rx_desc_idx];
    if (desc->Status & ETH_DMARXDESC_OWN) {
        *out_len = 0;
        return SYN_BUSY;
    }

    size_t pkt_len = (desc->Status >> 16) & 0x3FFF;
    if (pkt_len > max_len) {
        pkt_len = max_len;
    }

    memcpy(buf, Rx_Buff[s_rx_desc_idx], pkt_len);
    *out_len = pkt_len;

    desc->Status = ETH_DMARXDESC_OWN;
    ETH_DMA->RPDR = 0; /* Receive poll demand */

    s_rx_desc_idx = (s_rx_desc_idx + 1) % ETH_RX_DESC_NUM;
    return SYN_OK;
}

/* ═══════════════════════════════════════════════════════════════════════════
 *  Async DMA-Driven SPI Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

#include "syntropic/port/syn_port_spi_async.h"

#if defined(SYN_USE_SPI_ASYNC) && SYN_USE_SPI_ASYNC

SYN_Status syn_port_spi_xfer_async(const SYN_SPI_Xfer *xfer)
{
    if (xfer == NULL || xfer->bus != 0) {
        return SYN_ERROR;
    }

    SYN_Status st = syn_port_spi_transfer(xfer->bus, xfer->tx_buf, xfer->rx_buf, xfer->len);
    if (xfer->callback != NULL) {
        xfer->callback(xfer->bus, st, xfer->user_data);
    }
    return st;
}

SYN_Status syn_port_spi_cancel(uint8_t bus)
{
    if (bus != 0) {
        return SYN_ERROR;
    }
    return SYN_OK;
}

#endif /* SYN_USE_SPI_ASYNC */

/* ═══════════════════════════════════════════════════════════════════════════
 *  Async DMA-Driven I2C Implementation
 * ═══════════════════════════════════════════════════════════════════════════ */

#include "syntropic/port/syn_port_i2c_async.h"

#if defined(SYN_USE_I2C_ASYNC) && SYN_USE_I2C_ASYNC

SYN_Status syn_port_i2c_xfer_async(const SYN_I2C_Xfer *xfer)
{
    if (xfer == NULL || xfer->bus != 0) {
        return SYN_ERROR;
    }

    SYN_Status st = syn_port_i2c_transfer(xfer->bus, xfer->addr, xfer->tx_data, xfer->tx_len,
                                          xfer->rx_data, xfer->rx_len);
    if (xfer->callback != NULL) {
        xfer->callback(xfer->bus, st, xfer->user_data);
    }
    return st;
}

SYN_Status syn_port_i2c_cancel(uint8_t bus)
{
    if (bus != 0) {
        return SYN_ERROR;
    }
    return SYN_OK;
}

#endif /* SYN_USE_I2C_ASYNC */

/* ═══════════════════════════════════════════════════════════════════════════
 *  USB Device & Host Implementation (CH32V307 USBFS / USBHS)
 * ═══════════════════════════════════════════════════════════════════════════ */

#include "src/port/syn_port_usb.h"
#include "src/port/syn_port_usb_host.h"

typedef struct {
    volatile uint8_t BASE_CTRL;
    volatile uint8_t UDEV_CTRL;
    volatile uint8_t INT_EN;
    volatile uint8_t DEV_ADDR;
    volatile uint8_t MIS_ST;
    volatile uint8_t INT_FG;
    volatile uint8_t INT_ST;
    volatile uint8_t RX_LEN;
} USBFS_TypeDef;

#define USBFS ((USBFS_TypeDef *)0x50000000UL)

static uint8_t s_usb_dev_addr = 0;
static bool s_usb_vbus_on = false;

/* ── USB Device Port Functions ──────────────────────────────────────────── */

SYN_Status syn_port_usb_init(void)
{
    RCC_APB2PCENR |= (1U << 23);
    USBFS->BASE_CTRL = 0x00;
    return SYN_OK;
}

SYN_Status syn_port_usb_connect(void)
{
    USBFS->UDEV_CTRL |= (1U << 7);
    return SYN_OK;
}

SYN_Status syn_port_usb_disconnect(void)
{
    USBFS->UDEV_CTRL &= ~(1U << 7);
    return SYN_OK;
}

SYN_Status syn_port_usb_set_address(uint8_t addr)
{
    s_usb_dev_addr = addr & 0x7FU;
    USBFS->DEV_ADDR = s_usb_dev_addr;
    return SYN_OK;
}

SYN_Status syn_port_usb_ep_open(uint8_t ep_addr, uint8_t ep_type, uint16_t max_pkt)
{
    (void)ep_addr;
    (void)ep_type;
    (void)max_pkt;
    return SYN_OK;
}

SYN_Status syn_port_usb_ep_close(uint8_t ep_addr)
{
    (void)ep_addr;
    return SYN_OK;
}

SYN_Status syn_port_usb_ep_write(uint8_t ep_addr, const void *data, uint16_t len)
{
    (void)ep_addr;
    (void)data;
    (void)len;
    return SYN_OK;
}

SYN_Status syn_port_usb_ep_read(uint8_t ep_addr, void *buf, uint16_t max_len, uint16_t *out_len)
{
    (void)ep_addr;
    (void)buf;
    (void)max_len;
    if (out_len != NULL)
        *out_len = 0U;
    return SYN_BUSY;
}

SYN_Status syn_port_usb_ep_stall(uint8_t ep_addr)
{
    (void)ep_addr;
    return SYN_OK;
}

/* ── USB Host Port Functions ────────────────────────────────────────────── */

SYN_Status syn_port_usb_host_init(void)
{
    RCC_APB2PCENR |= (1U << 23);
    USBFS->BASE_CTRL = 0x80;
    return SYN_OK;
}

SYN_Status syn_port_usb_host_vbus(bool enable)
{
    s_usb_vbus_on = enable;
    (void)s_usb_vbus_on;
    return SYN_OK;
}

SYN_Status syn_port_usb_host_bus_reset(void)
{
    USBFS->BASE_CTRL |= (1U << 2);
    syn_port_delay_ms(10U);
    USBFS->BASE_CTRL &= ~(1U << 2);
    return SYN_OK;
}

bool syn_port_usb_host_device_attached(void)
{
    return (USBFS->MIS_ST & (1U << 2)) != 0U;
}

SYN_Status syn_port_usb_host_pipe_open(uint8_t pipe, uint8_t dev_addr, uint8_t ep_addr,
                                       uint8_t ep_type, uint16_t max_pkt)
{
    (void)pipe;
    (void)dev_addr;
    (void)ep_addr;
    (void)ep_type;
    (void)max_pkt;
    return SYN_OK;
}

SYN_Status syn_port_usb_host_pipe_close(uint8_t pipe)
{
    (void)pipe;
    return SYN_OK;
}

SYN_Status syn_port_usb_host_submit_setup(uint8_t pipe, const SYN_USB_SetupPacket *pkt)
{
    (void)pipe;
    (void)pkt;
    return SYN_OK;
}

SYN_Status syn_port_usb_host_submit_data(uint8_t pipe, uint8_t *buf, uint16_t len, bool is_in)
{
    (void)pipe;
    (void)buf;
    (void)len;
    (void)is_in;
    return SYN_OK;
}

bool syn_port_usb_host_xfer_done(uint8_t pipe)
{
    (void)pipe;
    return true;
}

SYN_Status syn_port_usb_host_xfer_result(uint8_t pipe, uint16_t *actual_len)
{
    (void)pipe;
    if (actual_len != NULL)
        *actual_len = 0U;
    return SYN_OK;
}

#endif /* CH32V307xx || CH32V307 */