15. DMA Manager Component

15.1. Overview

  • The DMA Manager component provides a unified resource management layer on top of the low-level DMA driver, responsible for the allocation, configuration, and lifecycle management of DMA channels. It supports multiple DMA instances (HDMA, XDMA, etc.) and offers interrupt callback registration and channel control capabilities. The following are the key features of this component:

    • Centralized DMA channel resource management

      • Automatically allocates and releases DMA channels, avoiding manual management and conflicts.

      • Supports requesting a channel from a specified DMA instance.

    • Unified channel configuration interface

      • Encapsulates DMA channel configuration (source/destination address, transfer width, burst size, work mode, etc.) through the dma_mgr_chn_conf_t structure, providing a one-time setup API dma_mgr_setup_channel.

    • Flexible interrupt callback mechanism

      • Supports separate registration of transfer complete (TC), half transfer complete (Half TC), error, and abort callbacks.

    • Supports advanced DMAV2 features

      • Infinite loop transfer, custom burst size, handshake options, byte order swap, etc.

15.3. API Call Flow Introduction

15.3.1. Initialize DMA Manager

  • Call the dma_mgr_init API to initialize the DMA manager context. This step must be performed before any DMA operations.

  • Prototype of the dma_mgr_init API:

    void dma_mgr_init(void);
    

15.3.2. Request DMA Resource

  • Call the dma_mgr_request_resource API to request an available DMA channel. The DMA manager automatically finds an unallocated channel among all DMA instances.

  • Prototype of the dma_mgr_request_resource API:

    hpm_stat_t dma_mgr_request_resource(dma_resource_t *resource);
    
    • Return Values:

      • status_success: DMA channel successfully allocated.

      • status_invalid_argument: The resource parameter is NULL.

      • status_dma_mgr_no_resource: All DMA channels are already occupied.

  • If you need to request a DMA channel from a specified DMA instance (e.g., HPM_HDMA or HPM_XDMA), use:

    hpm_stat_t dma_mgr_request_specified_resource(dma_resource_t *resource, DMA_Type *base);
    
  • Example: Request a DMA channel.

    dma_resource_t resource;
    
    if (dma_mgr_request_resource(&resource) != status_success) {
        printf("Failed to request DMA resource\n");
        return;
    }
    printf("DMA channel allocated: instance=%p, channel=%d\n", resource.base, resource.channel);
    

15.3.3. Get Default Channel Configuration

  • Call the dma_mgr_get_default_chn_config API to fill the dma_mgr_chn_conf_t structure with default values. You can then modify individual fields as needed.

  • Prototype of the dma_mgr_get_default_chn_config API:

    void dma_mgr_get_default_chn_config(dma_mgr_chn_conf_t *config);
    
    • Default values:

      • DMAMUX disabled, DMAMUX source = 0

      • Low priority

      • Source burst size = 1 transfer

      • Source/Destination work mode = Normal

      • Source/Destination width = Byte

      • Source/Destination address control = Increment

      • Source/Destination address = 0

      • Transfer size = 0, Linked pointer = 0

      • All interrupts masked

      • Infinite loop disabled, burst option = standard size, handshake option = one burst

15.3.4. Setup DMA Channel

  • Call the dma_mgr_setup_channel API to apply the configuration to the DMA channel. This API writes all the configuration fields to the hardware registers and optionally configures DMAMUX.

  • Prototype of the dma_mgr_setup_channel API:

    hpm_stat_t dma_mgr_setup_channel(const dma_resource_t *resource, dma_mgr_chn_conf_t *config);
    
    • Return Values:

      • status_success: Channel successfully configured.

      • status_invalid_argument: Invalid parameter.

15.3.5. Install DMA Transfer Complete Callback

  • Call the dma_mgr_install_chn_tc_callback API to register a callback for DMA transfer completion. This callback is invoked in the DMA ISR when the transfer is complete.

  • Prototype of the dma_mgr_install_chn_tc_callback API:

    hpm_stat_t dma_mgr_install_chn_tc_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
    
    • Return Values:

      • status_success: Callback successfully installed.

      • status_invalid_argument: Invalid parameter.

  • Similarly, you can also register callbacks for half transfer complete, error, and abort events:

    hpm_stat_t dma_mgr_install_chn_half_tc_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
    
    hpm_stat_t dma_mgr_install_chn_error_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
    
    hpm_stat_t dma_mgr_install_chn_abort_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
    

15.3.6. Enable DMA Channel and Start Transfer

  • Call the dma_mgr_enable_chn_irq API to enable the required interrupt sources for the channel.

  • Prototype of the dma_mgr_enable_chn_irq API:

    hpm_stat_t dma_mgr_enable_chn_irq(const dma_resource_t *resource, uint32_t irq_mask);
    
    • irq_mask can be a combination of DMA_MGR_INTERRUPT_MASK_TC, DMA_MGR_INTERRUPT_MASK_HALF_TC, DMA_MGR_INTERRUPT_MASK_ERROR, DMA_MGR_INTERRUPT_MASK_ABORT.

  • Call the dma_mgr_enable_dma_irq_with_priority API to enable the global DMA interrupt with a specified priority.

  • Prototype of the dma_mgr_enable_dma_irq_with_priority API:

    hpm_stat_t dma_mgr_enable_dma_irq_with_priority(const dma_resource_t *resource, uint32_t priority);
    
  • Call the dma_mgr_enable_channel API to start the DMA transfer.

  • Prototype of the dma_mgr_enable_channel API:

    hpm_stat_t dma_mgr_enable_channel(const dma_resource_t *resource);
    
    • Return Values:

      • status_success: Channel successfully enabled and transfer started.

      • status_invalid_argument: Invalid parameter.

15.3.7. Complete DMA Transfer Example

The following is a typical DMA transfer flow using the DMA Manager component:

#include "hpm_dma_mgr.h"
#include "hpm_uart_drv.h"

ATTR_PLACE_AT_NONCACHEABLE static char tx_buff[256];
static volatile bool tx_dma_complete;

/* DMA transfer complete callback */
static void tx_dma_complete_cb(DMA_Type *base, uint32_t channel, void *user_data)
{
    (void)base;
    (void)channel;
    (void)user_data;
    tx_dma_complete = true;
}

void uart_dma_send_example(void)
{
    dma_mgr_chn_conf_t chn_config;
    dma_resource_t tx_dma_resource;

    /* 1. Initialize DMA Manager */
    dma_mgr_init();

    /* 2. Request a DMA channel */
    if (dma_mgr_request_resource(&tx_dma_resource) != status_success) {
        printf("Failed to request DMA resource\n");
        return;
    }

    /* 3. Get default channel config */
    dma_mgr_get_default_chn_config(&chn_config);

    /* 4. Configure DMA channel for UART TX */
    chn_config.src_width = DMA_MGR_TRANSFER_WIDTH_BYTE;
    chn_config.dst_width = DMA_MGR_TRANSFER_WIDTH_BYTE;
    chn_config.src_addr = core_local_mem_to_sys_address(HPM_CORE0, (uint32_t)tx_buff);
    chn_config.dst_addr = (uint32_t)&HPM_UART0->DATA;
    chn_config.src_mode = DMA_MGR_HANDSHAKE_MODE_NORMAL;
    chn_config.dst_mode = DMA_MGR_HANDSHAKE_MODE_HANDSHAKE;
    chn_config.src_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_INCREMENT;
    chn_config.dst_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_FIXED;
    chn_config.size_in_byte = sizeof(tx_buff);
    chn_config.en_dmamux = true;
    chn_config.dmamux_src = HPM_DMA_SRC_UART0_TX;
    chn_config.interrupt_mask = DMA_MGR_INTERRUPT_MASK_NONE;

    dma_mgr_setup_channel(&tx_dma_resource, &chn_config);

    /* 5. Install transfer complete callback */
    dma_mgr_install_chn_tc_callback(&tx_dma_resource, tx_dma_complete_cb, NULL);

    /* 6. Enable channel interrupt and global DMA interrupt */
    dma_mgr_enable_chn_irq(&tx_dma_resource, DMA_MGR_INTERRUPT_MASK_TC);
    dma_mgr_enable_dma_irq_with_priority(&tx_dma_resource, 1);

    /* 7. Start DMA transfer */
    dma_mgr_enable_channel(&tx_dma_resource);

    /* 8. Wait for transfer completion */
    while (!tx_dma_complete) {
    }
    printf("UART DMA transfer complete\n");
}

15.3.8. Channel Control APIs

  • Disable Channel: Stop DMA channel transfer.

    hpm_stat_t dma_mgr_disable_channel(const dma_resource_t *resource);
    
  • Abort Transfer: Forcefully abort a DMA channel transfer.

    hpm_stat_t dma_mgr_abort_chn_transfer(const dma_resource_t *resource);
    
  • Check Transfer Status: Query the current status of a DMA channel.

    hpm_stat_t dma_mgr_check_chn_transfer_status(const dma_resource_t *resource, uint32_t *status);
    
  • Get Remaining Transfer Size: Query the remaining bytes to be transferred.

    hpm_stat_t dma_mgr_get_chn_remaining_transize(const dma_resource_t *resource, uint32_t *size);
    
  • Set Transfer Size: Modify the transfer size of a channel.

    hpm_stat_t dma_mgr_set_chn_transize(const dma_resource_t *resource, uint32_t size);
    
  • Release DMA Resource: Release the DMA channel back to the manager.

    hpm_stat_t dma_mgr_release_resource(const dma_resource_t *resource);
    

15.3.9. Per-Field Configuration APIs

If you do not wish to use the one-time dma_mgr_setup_channel, you can also configure each field individually:

dma_mgr_set_chn_src_addr(&resource, src_addr);
dma_mgr_set_chn_dst_addr(&resource, dst_addr);
dma_mgr_set_chn_src_width(&resource, width);
dma_mgr_set_chn_dst_width(&resource, width);
dma_mgr_set_chn_src_work_mode(&resource, mode);
dma_mgr_set_chn_dst_work_mode(&resource, mode);
dma_mgr_set_chn_src_addr_ctrl(&resource, addr_ctrl);
dma_mgr_set_chn_dst_addr_ctrl(&resource, addr_ctrl);
dma_mgr_set_chn_src_burst_size(&resource, burstsize);
dma_mgr_set_chn_transize(&resource, size);
dma_mgr_set_chn_priority(&resource, priority);
dma_mgr_set_chn_infinite_loop_mode(&resource, enable);
dma_mgr_set_chn_handshake_option(&resource, handshake_opt);

Note

  • The DMA Manager is a foundational component in the SDK, used by the following higher-level components:

    • components/spi: The SPI component manages SPI DMA transfers via DMA Manager.

    • components/i2c: The I2C component manages I2C DMA transfers via DMA Manager.

    • components/serial_nor: The Serial NOR component manages Flash DMA transfers via DMA Manager.

    • components/i2s_over_spi: The I2S over SPI component manages audio DMA transfers via DMA Manager.