DMA (Direct Memory Access)

group group_hal_dma

High level interface for interacting with the direct memory access (DMA).

The DMA driver allows for initializing and configuring a DMA channel in order to trigger data transfers to and from memory and peripherals. The transfers occur independently of the CPU and are triggered in software. Multiple channels can be active at the same time each with their own user-selectable priority and transfer characteristics.

Features

  • CPU independent memory access

  • Access to memory and peripherals

  • Multiple independent channels

  • Configurable transer sizes and bursts

  • Configurable priorities

  • Event completion notification

Quick Start

See Snippet 1: Simple DMA initialization and transfer for a code snippet that sets up a DMA transfer to move memory from one location to another.

Code snippets

note

Error handling code has been intentionally left out of snippets to highlight API usage.

Snippet 1: Simple DMA initialization and transfer

The following snippet initializes a DMA channel and uses it to transfer a a single block of memory. The DMA channel is reserved by calling cyhal_dma_init. It then needs to be configured with cyhal_dma_configure and then the transfer is started with cyhal_dma_start_transfer.

If the DMA channel is not needed anymore, it can be released by calling

cyhal_dma_free

    cy_rslt_t       rslt;
    cyhal_dma_t     dma;
    cyhal_dma_cfg_t cfg =
    {
        .src_addr       = 0x8000000,               // Start from address
        .src_increment  = 1,                       // Increment source by 1 word
        .dst_addr       = 0x8001000,               // Destination from address
        .dst_increment  = 1,                       // Increment destination by 1 word
        .transfer_width = 32,                      // 32 bit transfer
        .length         = 0x10,                    // Transfer 64 bytes (16 transfers of 4 bytes)
        .burst_size     = 0,                       // Transfer everything in a single burst
        .action         = CYHAL_DMA_TRANSFER_FULL, // Notify when everything is done
    };

    // Allocate the DMA channel for use
    rslt = cyhal_dma_init(&dma, CYHAL_DMA_PRIORITY_DEFAULT, CYHAL_DMA_DIRECTION_MEM2MEM);

    // Configure the channel for the upcoming transfer
    rslt = cyhal_dma_configure(&dma, &cfg);

    // Begin the transfer
    rslt = cyhal_dma_start_transfer(&dma);


    // If the DMA channel is no longer needed, it can be freed up for other uses
    if (!cyhal_dma_is_busy(&dma))
    {
        cyhal_dma_free(&dma);
    }

Snippet 2: Configuring the DMA channel based on memory requirements

cyhal_dma_configure can be used after DMA initialization to handle a variety of memory layouts.

    #define BUFFER_SIZE (32)
    #define PERIPHERAL_FIFO_ADDR (0x4001000)

    cy_rslt_t   rslt;
    cyhal_dma_t dma;
    uint32_t    buffer[BUFFER_SIZE];

    // Allocate the DMA channel for transfering from memory to a peripheral
    rslt = cyhal_dma_init(&dma, CYHAL_DMA_PRIORITY_DEFAULT, CYHAL_DMA_DIRECTION_MEM2PERIPH);

    cyhal_dma_cfg_t cfg =
    {
        .src_addr       = (uint32_t)buffer,        // Start from address
        .src_increment  = 1,                       // Increment source by 1 word
        .dst_addr       = PERIPHERAL_FIFO_ADDR,    // Destination from address
        .dst_increment  = 0,                       // Don't increment the destination
        .transfer_width = 32,                      // 32 bit transfer
        .length         = BUFFER_SIZE,             // Transfer 64 bytes (16 transfers of 4 bytes)
        .burst_size     = 0,                       // Transfer everything in a single burst
        .action         = CYHAL_DMA_TRANSFER_FULL, // Notify when everything is done
    };
    rslt = cyhal_dma_configure(&dma, &cfg);

Snippet 3: Interrupts and retriggering DMA transfers

DMA events like transfer complete or error events can be used to trigger a callback function.

This snippet uses

cyhal_dma_configure to break the full transfer into multiple bursts. This allows higher priority items access to the memory bus if necessary while the DMA operation is still in progress. It then uses cyhal_dma_enable_event() to enable the transfer complete event to trigger the callback function registered by cyhal_dma_register_callback().

void dma_event_callback(void* callback_arg, cyhal_dma_event_t event)
{
    CY_UNUSED_PARAMETER(event);
    cyhal_dma_t* dma = (cyhal_dma_t*)callback_arg;

    // Do work specific to each burst complete

    // If all bursts are complete, start another
    if (!cyhal_dma_is_busy(dma))
    {
        cyhal_dma_start_transfer(dma);
    }
}


void snippet_cyhal_dma_events()
{
    cy_rslt_t       rslt;
    cyhal_dma_t     dma;
    cyhal_dma_cfg_t cfg =
    {
        .src_addr       = 0x8000000,                // Start from address
        .src_increment  = 1,                        // Increment source by 1 word
        .dst_addr       = 0x8001000,                // Destination from address
        .dst_increment  = 1,                        // Increment destination by 1 word
        .transfer_width = 32,                       // 32 bit transfer
        .length         = 0x10,                     // Transfer 64 bytes (16 transfers of 4 bytes)
        .burst_size     = 4,                        // Transfer 4 words a a time
        .action         = CYHAL_DMA_TRANSFER_BURST, // Notify when each burst is done
    };

    // Allocate the DMA channel for use
    rslt = cyhal_dma_init(&dma, CYHAL_DMA_PRIORITY_LOW, CYHAL_DMA_DIRECTION_MEM2MEM);
    CY_ASSERT(CY_RSLT_SUCCESS == rslt);

    // Configure the channel for the upcoming transfer
    rslt = cyhal_dma_configure(&dma, &cfg);

    // Register an event callback handler and enable events
    cyhal_dma_register_callback(&dma, &dma_event_callback, &dma);
    cyhal_dma_enable_event(&dma, CYHAL_DMA_TRANSFER_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true);

    // Begin the transfer
    rslt = cyhal_dma_start_transfer(&dma);
}

Defines

cyhal_dma_init(obj, priority, direction)

Initialize the DMA peripheral.

Return

The status of the init request

Parameters
  • [out] obj: Pointer to a DMA object. The caller must allocate the memory for this object but the init function will initialize its contents.

  • [in] priority: The priority of this DMA operation relative to others. The number of priority levels which are supported is hardware dependent. All implementations define a CYHAL_DMA_PRIORITY_DEFAULT constant which is always valid. If supported, implementations will also define CYHAL_DMA_PRIORITY_HIGH, CYHAL_DMA_PRIORITY_MEDIUM, and CYHAL_DMA_PRIORITY_LOW. The behavior of any other value is implementation defined. See the implementation-specific DMA documentation for more details.

  • [in] direction: The direction memory is copied

Typedefs

typedef void (*cyhal_dma_event_callback_t)(void *callback_arg, cyhal_dma_event_t event)

Event handler for DMA interrupts.

Enums

enum cyhal_dma_direction_t

cyhal_dma_direction_t: Direction for DMA transfers.

Values:

enumerator CYHAL_DMA_DIRECTION_MEM2MEM

Memory to memory.

enumerator CYHAL_DMA_DIRECTION_MEM2PERIPH

Memory to peripheral.

enumerator CYHAL_DMA_DIRECTION_PERIPH2MEM

Peripheral to memory.

enumerator CYHAL_DMA_DIRECTION_PERIPH2PERIPH

Peripheral to peripheral.

enum cyhal_dma_event_t

cyhal_dma_event_t: Flags enum of DMA events.

Multiple events can be enabled.

Values:

enumerator CYHAL_DMA_NO_INTR

No interrupt.

enumerator CYHAL_DMA_TRANSFER_COMPLETE

Indicates that a burst or full transfer has completed.

enumerator CYHAL_DMA_SRC_BUS_ERROR

Indicates that there is a source bus error.

enumerator CYHAL_DMA_DST_BUS_ERROR

Indicates that there is a destination bus error.

enumerator CYHAL_DMA_SRC_MISAL

Indicates that the source address is not aligned.

enumerator CYHAL_DMA_DST_MISAL

Indicates that the destination address is not aligned.

enumerator CYHAL_DMA_CURR_PTR_NULL

Indicates that the current descriptor pointer is null.

enumerator CYHAL_DMA_ACTIVE_CH_DISABLED

Indicates that the active channel is disabled.

enumerator CYHAL_DMA_DESCR_BUS_ERROR

Indicates that there has been a descriptor bus error.

enum cyhal_dma_input_t

cyhal_dma_input_t: Specifies the transfer type to trigger when an input signal is received.

Values:

enumerator CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT

Transfer a single element when an input signal is received.

enumerator CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST

Transfer a single burst when an input signal is received.

enumerator CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS

Transfer all elements when an input signal is received.

enum cyhal_dma_output_t

cyhal_dma_output_t: Specifies the transfer completion event that triggers a signal output.

Values:

enumerator CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT

Trigger an output when a single element is transferred.

enumerator CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST

Trigger an output when a single burst is transferred.

enumerator CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS

Trigger an output when all elements are transferred.

enum cyhal_dma_transfer_action_t

cyhal_dma_transfer_action_t: If burst_size is used, selects whether a single trigger of the channel transfers a single burst of burst_size or a full transfer of size length (that is, every burst is triggered).

This will also set the initial transfer type that will trigger an output signal on completion.

Values:

enumerator CYHAL_DMA_TRANSFER_BURST

A single burst is triggered and a transfer completion event will occur after the burst.

enumerator CYHAL_DMA_TRANSFER_FULL

All bursts are triggered and a single transfer completion event will occur at the end of all of them.

Functions

cy_rslt_t cyhal_dma_init_adv(cyhal_dma_t *obj, cyhal_dma_src_t *src, cyhal_dma_dest_t *dest, cyhal_source_t *dest_source, uint8_t priority, cyhal_dma_direction_t direction)

Initialize the DMA peripheral.

If a source signal is provided for src, this will connect the provided signal to the DMA just as would be done by calling cyhal_dma_connect_digital. Similarly, if a destination target is provided for dest this will enable the specified output just as would be done by calling cyhal_dma_enable_output.

Return

The status of the init request

Parameters
  • [out] obj: Pointer to a DMA object. The caller must allocate the memory for this object but the init function will initialize its contents.

  • [in] src: An optional source signal to connect to the DMA

  • [in] dest: An optional destination singal to drive from the DMA

  • [out] dest_source: An optional pointer to user-allocated source signal object which will be initialized by enable_output. If dest is non-null, this must also be non-null. dest_source should be passed to (dis)connect_digital functions to (dis)connect the associated endpoints.

  • [in] priority: The priority of this DMA operation relative to others. The number of priority levels which are supported is hardware dependent. All implementations define a CYHAL_DMA_PRIORITY_DEFAULT constant which is always valid. If supported, implementations will also define CYHAL_DMA_PRIORITY_HIGH, CYHAL_DMA_PRIORITY_MEDIUM, and CYHAL_DMA_PRIORITY_LOW. The behavior of any other value is implementation defined. See the implementation-specific DMA documentation for more details.

  • [in] direction: The direction memory is copied

void cyhal_dma_free(cyhal_dma_t *obj)

Free the DMA object.

Freeing a DMA object while a transfer is in progress (see cyhal_dma_is_busy) is invalid.

Parameters
  • [inout] obj: The DMA object

cy_rslt_t cyhal_dma_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg)

Setup a DMA descriptor for specified resource.

Return

The status of the configure request

Parameters
  • [in] obj: The DMA object

  • [in] cfg: Configuration parameters for the transfer

cy_rslt_t cyhal_dma_start_transfer(cyhal_dma_t *obj)

Initiates DMA channel transfer for specified DMA object.

Return

The status of the start_transfer request

Parameters
  • [in] obj: The DMA object

bool cyhal_dma_is_busy(cyhal_dma_t *obj)

Checks whether a transfer is pending or running on the DMA channel.

Return

True if DMA channel is busy

Parameters
  • [in] obj: The DMA object

void cyhal_dma_register_callback(cyhal_dma_t *obj, cyhal_dma_event_callback_t callback, void *callback_arg)

Register a DMA callback handler.

This function will be called when one of the events enabled by cyhal_dma_enable_event occurs.

Parameters
  • [in] obj: The DMA object

  • [in] callback: The callback handler which will be invoked when an event triggers

  • [in] callback_arg: Generic argument that will be provided to the callback when called

void cyhal_dma_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable)

Configure DMA event enablement.

When an enabled event occurs, the function specified by cyhal_dma_register_callback will be called.

Parameters
  • [in] obj: The DMA object

  • [in] event: The DMA event type

  • [in] intr_priority: The priority for NVIC interrupt events. The priority from the most recent call will take precedence, i.e all events will have the same priority.

  • [in] enable: True to turn on interrupts, False to turn off

cy_rslt_t cyhal_dma_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)

Connects a source signal and enables the specified input to the DMA channel.

Return

The status of the connection

Parameters
  • [in] obj: The DMA object

  • [in] source: Source signal obtained from another driver’s cyhal_<PERIPH>_enable_output

  • [in] input: Which input to enable

cy_rslt_t cyhal_dma_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)

Enables the specified output signal from a DMA channel that is triggered when a transfer is completed.

Return

The status of the output enable

Parameters
  • [in] obj: The DMA object

  • [in] output: Which event triggers the output

  • [out] source: Pointer to user-allocated source signal object which will be initialized by enable_output. source should be passed to (dis)connect_digital functions to (dis)connect the associated endpoints.

cy_rslt_t cyhal_dma_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)

Disconnects a source signal and disables the specified input to the DMA channel.

Return

The status of the disconnect

Parameters
  • [in] obj: The DMA object

  • [in] source: Source signal from cyhal_<PERIPH>_enable_output to disable

  • [in] input: Which input to disable

cy_rslt_t cyhal_dma_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)

Disables the specified output signal from a DMA channel.

Return

The status of the disablement

Parameters
  • [in] obj: The DMA object

  • [in] output: Which output to disable

struct cyhal_dma_cfg_t
#include <cyhal_dma.h>

Configuration of a DMA channel.

When configuring address, increments, and transfer width keep in mind your hardware may have more stringent address and data alignment requirements.

Public Members

uint32_t src_addr

Source address.

int16_t src_increment

Source address auto increment amount in multiples of transfer_width.

uint32_t dst_addr

Destination address.

int16_t dst_increment

Destination address auto increment amount in multiples of transfer_width.

uint8_t transfer_width

Transfer width in bits. Valid values are: 8, 16, or 32.

uint32_t length

Number of elements to be transferred in total.

uint32_t burst_size

Number of elements to be transferred per trigger. If set to 0 every element is transferred, otherwise burst_size must evenly divide length.

cyhal_dma_transfer_action_t action

Sets the behavior of the channel when triggered (using start_transfer). Ignored if burst_size is not configured.

struct cyhal_dma_src_t
#include <cyhal_dma.h>

DMA input connection information to setup while initializing the driver.

Public Members

cyhal_source_t source

Source of signal to DMA; obtained from another driver’s cyhal_<PERIPH>_enable_output.

cyhal_dma_input_t input

DMA input signal to be driven.

struct cyhal_dma_dest_t
#include <cyhal_dma.h>

DMA output connection information to setup while initializing the driver.

Public Members

cyhal_dma_output_t output

Output signal of DMA.

cyhal_dest_t dest

Destination of DMA signal.