Skip to content

File syn_dma.c

File List > drivers > syn_dma.c

Go to the documentation of this file

#include "syn_dma.h"

#include "../common/syn_compiler.h"
#include "../util/syn_assert.h"

#include <string.h>

SYN_Status syn_dma_init(SYN_DMA *dma, const SYN_DMA_Config *cfg)
{
    if (!dma || !cfg) {
        return SYN_INVALID_PARAM;
    }

    if (cfg->data_size != SYN_DMA_SIZE_8BIT && cfg->data_size != SYN_DMA_SIZE_16BIT &&
        cfg->data_size != SYN_DMA_SIZE_32BIT) {
        return SYN_INVALID_PARAM;
    }

    memset(dma, 0, sizeof(*dma));
    dma->cfg = *cfg;
    dma->state = SYN_DMA_STATE_IDLE;

    return SYN_OK;
}

SYN_Status syn_dma_start(SYN_DMA *dma, const void *src, void *dst, size_t count)
{
    if (!dma || !src || !dst || count == 0) {
        return SYN_INVALID_PARAM;
    }

    if (dma->state == SYN_DMA_STATE_BUSY || syn_port_dma_is_busy(dma->cfg.channel_id)) {
        return SYN_BUSY;
    }

    /* Safety Safeguard 1: Verify Memory Address Alignment */
    uintptr_t src_addr = (uintptr_t)src;
    uintptr_t dst_addr = (uintptr_t)dst;
    size_t align_mask = (size_t)dma->cfg.data_size - 1U;

    if ((src_addr & align_mask) != 0 || (dst_addr & align_mask) != 0) {
        return SYN_INVALID_PARAM;
    }

    size_t total_bytes = count * (size_t)dma->cfg.data_size;

    /* Safety Safeguard 2: Clean source D-Cache and Invalidate destination D-Cache */
    syn_port_cache_clean(src, total_bytes);
    syn_port_cache_invalidate(dst, total_bytes);

    /* Prepare port transfer request */
    SYN_PortDmaTransfer xfer = {.channel_id = dma->cfg.channel_id,
                                .dir = dma->cfg.dir,
                                .data_size = dma->cfg.data_size,
                                .src_inc = dma->cfg.src_inc,
                                .dst_inc = dma->cfg.dst_inc,
                                .src = src,
                                .dst = dst,
                                .count = count};

    dma->current_src = src;
    dma->current_dst = dst;
    dma->current_len = total_bytes;
    dma->state = SYN_DMA_STATE_BUSY;

    /* Compiler memory barrier before hardware trigger */
    SYN_COMPILER_BARRIER();

    SYN_Status st = syn_port_dma_start(&xfer);
    if (st != SYN_OK) {
        dma->state = SYN_DMA_STATE_ERROR;
        dma->errors_cnt++;
        return st;
    }

    return SYN_OK;
}

SYN_Status syn_dma_stop(SYN_DMA *dma)
{
    if (!dma)
        return SYN_INVALID_PARAM;

    syn_port_dma_stop(dma->cfg.channel_id);
    dma->state = SYN_DMA_STATE_IDLE;
    return SYN_OK;
}

SYN_DMA_State syn_dma_get_state(const SYN_DMA *dma)
{
    if (!dma)
        return SYN_DMA_STATE_ERROR;
    return dma->state;
}

void syn_dma_isr_handler(SYN_DMA *dma, SYN_DMA_Event event)
{
    if (!dma)
        return;

    if (event & SYN_DMA_EVENT_ERROR) {
        syn_port_dma_stop(dma->cfg.channel_id);
        dma->state = SYN_DMA_STATE_ERROR;
        dma->errors_cnt++;
    } else if (event & SYN_DMA_EVENT_COMPLETE) {
        syn_port_dma_stop(dma->cfg.channel_id);
        /* Clean/Invalidate cache after hardware write completion */
        syn_port_cache_invalidate(dma->current_dst, dma->current_len);
        dma->state = SYN_DMA_STATE_COMPLETE;
        dma->transfers_cnt++;
    }

    /* Fire user completion/event callback */
    if (dma->cfg.callback) {
        dma->cfg.callback(dma, event, dma->cfg.user_ctx);
    }
}

/* ── Circular DMA Ring Buffer Implementation ─────────────────────────────── */

SYN_Status syn_dma_ringbuf_init(SYN_DMA_RingBuf *r, SYN_DMA *dma, uint8_t *buf, size_t capacity)
{
    if (!r || !dma || !buf || capacity == 0) {
        return SYN_INVALID_PARAM;
    }

    memset(r, 0, sizeof(*r));
    r->dma = dma;
    r->buf = buf;
    r->capacity = capacity;
    r->tail = 0;

    return SYN_OK;
}

SYN_Status syn_dma_ringbuf_start(SYN_DMA_RingBuf *r, const void *periph_src)
{
    if (!r || !periph_src) {
        return SYN_INVALID_PARAM;
    }

    r->tail = 0;
    return syn_dma_start(r->dma, periph_src, r->buf, r->capacity);
}

size_t syn_dma_ringbuf_bytes_available(const SYN_DMA_RingBuf *r)
{
    if (!r || !r->dma || r->capacity == 0) {
        return 0;
    }

    uint32_t remaining = syn_port_dma_get_counter(r->dma->cfg.channel_id);
    size_t head = (remaining <= r->capacity) ? (r->capacity - remaining) : 0;
    size_t tail = r->tail;

    if (head >= tail) {
        return head - tail;
    } else {
        return r->capacity - (tail - head);
    }
}

size_t syn_dma_ringbuf_read(SYN_DMA_RingBuf *r, uint8_t *dest, size_t len)
{
    if (!r || !dest || len == 0 || r->capacity == 0) {
        return 0;
    }

    size_t avail = syn_dma_ringbuf_bytes_available(r);
    if (avail == 0) {
        return 0;
    }

    size_t to_read = (len < avail) ? len : avail;
    size_t tail = r->tail;
    size_t read_bytes = 0;

    while (read_bytes < to_read) {
        size_t chunk = r->capacity - tail;
        if (chunk > to_read - read_bytes) {
            chunk = to_read - read_bytes;
        }

        memcpy(dest + read_bytes, r->buf + tail, chunk);
        tail = (tail + chunk) % r->capacity;
        read_bytes += chunk;
    }

    r->tail = tail;
    return read_bytes;
}