Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / include / cyhal_dma.h
@Dustin Crossman Dustin Crossman on 4 Jun 2021 16 KB Fix file modes.
/***************************************************************************//**
* \file cyhal_dma.h
*
* \brief
* Provides a high level interface for interacting with the Cypress DMA.
* This interface abstracts out the chip specific details. If any chip specific
* functionality is necessary, or performance is critical the low level functions
* can be used directly.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

/**
 * \addtogroup group_hal_dma DMA (Direct Memory Access)
 * \ingroup group_hal
 * \{
 * 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.
 *
 * \section section_dma_features Features
 * * CPU independent memory access
 * * Access to memory and peripherals
 * * Multiple independent channels
 * * Configurable transer sizes and bursts
 * * Configurable priorities
 * * Event completion notification
 *
 * \section section_dma_quickstart Quick Start
 *
 * See \ref subsection_dma_snippet_1 for a code snippet that sets up a DMA
 * transfer to move memory from one location to another.
 *
 * \section section_dma_snippets Code snippets
 * \note Error handling code has been intentionally left out of snippets to highlight API usage.
 *
 * \subsection subsection_dma_snippet_1 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 \ref cyhal_dma_init. It then needs to be configured with
 * \ref cyhal_dma_configure and then the transfer is started with \ref cyhal_dma_start_transfer.<br>
 * If the DMA channel is not needed anymore, it can be released by calling \ref cyhal_dma_free
 *
 * \snippet hal_dma.c snippet_cyhal_dma_simple_init
 *
 *
 * \subsection subsection_dma_snippet_2 Snippet 2: Configuring the DMA channel based on memory requirements
 * \ref cyhal_dma_configure can be used after DMA initialization to handle a variety of memory layouts.
 *
 * \snippet hal_dma.c snippet_cyhal_dma_configure
 *
 *
 * \subsection subsection_dma_snippet_3 Snippet 3: Interrupts and retriggering DMA transfers
 * DMA events like transfer complete or error events can be used to trigger a callback function. <br>
 * This snippet uses \ref 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 \ref cyhal_dma_enable_event() to enable the transfer complete event to
 * trigger the callback function registered by \ref cyhal_dma_register_callback().
 *
 * \snippet hal_dma.c snippet_cyhal_dma_events
 */

#pragma once

#include <stdbool.h>
#include <stdint.h>
#include "cy_result.h"
#include "cyhal_hw_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

/** \addtogroup group_hal_results_dma DMA HAL Results
 *  DMA specific return codes
 *  \ingroup group_hal_results
 *  \{ *//**
 */

/** Invalid transfer width parameter error */
#define CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_WIDTH       \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 0))
/** Invalid parameter error */
#define CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER            \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 1))
/** Invalid priority parameter error */
#define CYHAL_DMA_RSLT_ERR_INVALID_PRIORITY             \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 2))
/** Invalid src or dst addr alignment error */
#define CYHAL_DMA_RSLT_ERR_INVALID_ALIGNMENT            \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 3))
/** Invalid burst_size paramenter error */
#define CYHAL_DMA_RSLT_ERR_INVALID_BURST_SIZE           \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 4))
/** Channel busy error */
#define CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY                 \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_DMA, 5))
/** Transfer has already been started warning */
#define CYHAL_DMA_RSLT_WARN_TRANSFER_ALREADY_STARTED    \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_WARNING, CYHAL_RSLT_MODULE_DMA, 6))
/** Unsupported hardware error */
#define CYHAL_DMA_RSLT_FATAL_UNSUPPORTED_HARDWARE       \
    (CYHAL_RSLT_CREATE(CY_RSLT_TYPE_FATAL, CYHAL_RSLT_MODULE_DMA, 7))

/**
 * \}
 */

/** Direction for DMA transfers. */
typedef enum
{
    CYHAL_DMA_DIRECTION_MEM2MEM,       //!< Memory to memory
    CYHAL_DMA_DIRECTION_MEM2PERIPH,    //!< Memory to peripheral
    CYHAL_DMA_DIRECTION_PERIPH2MEM,    //!< Peripheral to memory
    CYHAL_DMA_DIRECTION_PERIPH2PERIPH, //!< Peripheral to peripheral
} cyhal_dma_direction_t;

/** Flags enum of DMA events. Multiple events can be enabled. */
typedef enum
{
    CYHAL_DMA_NO_INTR             = 0,      //!< No interrupt
    CYHAL_DMA_TRANSFER_COMPLETE   = 1 << 0, //!< Indicates that a burst or full transfer has completed
    CYHAL_DMA_SRC_BUS_ERROR       = 1 << 1, //!< Indicates that there is a source bus error
    CYHAL_DMA_DST_BUS_ERROR       = 1 << 2, //!< Indicates that there is a destination bus error
    CYHAL_DMA_SRC_MISAL           = 1 << 3, //!< Indicates that the source address is not aligned
    CYHAL_DMA_DST_MISAL           = 1 << 4, //!< Indicates that the destination address is not aligned
    CYHAL_DMA_CURR_PTR_NULL       = 1 << 5, //!< Indicates that the current descriptor pointer is null
    CYHAL_DMA_ACTIVE_CH_DISABLED  = 1 << 6, //!< Indicates that the active channel is disabled
    CYHAL_DMA_DESCR_BUS_ERROR     = 1 << 7, //!< Indicates that there has been a descriptor bus error
} cyhal_dma_event_t;

/** Specifies the transfer type to trigger when an input signal is received.
 * */
typedef enum
{
    CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT, //!< Transfer a single element when an input signal is received
    CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST,   //!< Transfer a single burst when an input signal is received
    CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS,   //!< Transfer all elements when an input signal is received
} cyhal_dma_input_t;

/** Specifies the transfer completion event that triggers a signal output.  */
typedef enum
{
    CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT, //!< Trigger an output when a single element is transferred
    CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST,   //!< Trigger an output when a single burst is transferred
    CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS,   //!< Trigger an output when all elements are transferred
} cyhal_dma_output_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.*/
typedef enum
{
    CYHAL_DMA_TRANSFER_BURST, //!< A single burst is triggered and a transfer completion event will occur after the burst
    CYHAL_DMA_TRANSFER_FULL,  //!< All bursts are triggered and a single transfer completion event will occur at the end of all of them
} cyhal_dma_transfer_action_t;

/** \brief 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. */
typedef struct
{
    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.
} cyhal_dma_cfg_t;

/** Event handler for DMA interrupts */
typedef void (*cyhal_dma_event_callback_t)(void *callback_arg, cyhal_dma_event_t event);

/** DMA input connection information to setup while initializing the driver. */
typedef struct
{
    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
} cyhal_dma_src_t;

/** DMA output connection information to setup while initializing the driver. */
typedef struct
{
    cyhal_dma_output_t  output; //!< Output signal of DMA
    cyhal_dest_t        dest;   //!< Destination of DMA signal
} cyhal_dma_dest_t;

/** Initialize the DMA peripheral.
 *
 * If a source signal is provided for \p src, this will connect the provided signal to the DMA
 * just as would be done by calling \ref cyhal_dma_connect_digital. Similarly, if a destination
 * target is provided for \p dest this will enable the specified output just as would be done
 * by calling \ref cyhal_dma_enable_output.
 * @param[out] obj  Pointer to a DMA object. The caller must allocate the memory
 *  for this object but the init function will initialize its contents.
 * @param[in]  src          An optional source signal to connect to the DMA
 * @param[in]  dest         An optional destination singal to drive from the DMA
 * @param[out] dest_source  An optional pointer to user-allocated source signal object which
 * will be initialized by enable_output. If \p dest is non-null, this must also be non-null.
 * \p dest_source should be passed to (dis)connect_digital functions to (dis)connect the
 * associated endpoints.
 * @param[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.
 * @param[in]  direction    The direction memory is copied
 * @return The status of the init request
 */
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.
 *
 * @param[out] obj  Pointer to a DMA object. The caller must allocate the memory
 *  for this object but the init function will initialize its contents.
 * @param[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.
 * @param[in]  direction    The direction memory is copied
 * @return The status of the init request
 */
#define cyhal_dma_init(obj, priority, direction)    (cyhal_dma_init_adv(obj, NULL, NULL, NULL, priority, direction))

/** Free the DMA object. Freeing a DMA object while a transfer is in
    progress (see @ref cyhal_dma_is_busy) is invalid.
 *
 * @param[in,out] obj The DMA object
 */
void cyhal_dma_free(cyhal_dma_t *obj);

/** Setup a DMA descriptor for specified resource
 *
 * @param[in] obj    The DMA object
 * @param[in] cfg    Configuration parameters for the transfer
 * @return The status of the configure request
 */
cy_rslt_t cyhal_dma_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg);

/** Initiates DMA channel transfer for specified DMA object
 *
 * @param[in] obj    The DMA object
 * @return The status of the start_transfer request
 */
cy_rslt_t cyhal_dma_start_transfer(cyhal_dma_t *obj);

/** Checks whether a transfer is pending or running on the DMA channel
 *
 * @param[in] obj    The DMA object
 * @return True if DMA channel is busy
 */
bool cyhal_dma_is_busy(cyhal_dma_t *obj);

/** Register a DMA callback handler.
 *
 * This function will be called when one of the events enabled by \ref cyhal_dma_enable_event occurs.
 *
 * @param[in] obj          The DMA object
 * @param[in] callback     The callback handler which will be invoked when an event triggers
 * @param[in] callback_arg Generic argument that will be provided to the callback when called
 */
void cyhal_dma_register_callback(cyhal_dma_t *obj, cyhal_dma_event_callback_t callback, void *callback_arg);

/** Configure DMA event enablement.
 *
 * When an enabled event occurs, the function specified by \ref cyhal_dma_register_callback will be called.
 *
 * @param[in] obj            The DMA object
 * @param[in] event          The DMA event type
 * @param[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.
 * @param[in] enable         True to turn on interrupts, False to turn off
 */
void cyhal_dma_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable);

/** Connects a source signal and enables the specified input to the DMA
 * channel
 *
 * @param[in] obj         The DMA object
 * @param[in] source      Source signal obtained from another driver's cyhal_<PERIPH>_enable_output
 * @param[in] input       Which input to enable
 * @return The status of the connection
 * */
cy_rslt_t cyhal_dma_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input);

/** Enables the specified output signal from a DMA channel that is triggered when a transfer is completed
 *
 * @param[in]  obj         The DMA object
 * @param[in]  output      Which event triggers the output
 * @param[out] source      Pointer to user-allocated source signal object which
 * will be initialized by enable_output. \p source should be passed to
 * (dis)connect_digital functions to (dis)connect the associated endpoints.
 * @return The status of the output enable
 * */
cy_rslt_t cyhal_dma_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source);

/** Disconnects a source signal and disables the specified input to the DMA channel
 *
 * @param[in] obj         The DMA object
 * @param[in] source      Source signal from cyhal_<PERIPH>_enable_output to disable
 * @param[in] input       Which input to disable
 * @return The status of the disconnect
 * */
cy_rslt_t cyhal_dma_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input);

/** Disables the specified output signal from a DMA channel
 *
 * @param[in]  obj         The DMA object
 * @param[in]  output      Which output to disable
 * @return The status of the disablement
 * */
cy_rslt_t cyhal_dma_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output);

#if defined(__cplusplus)
}
#endif

#ifdef CYHAL_DMA_IMPL_HEADER
#include CYHAL_DMA_IMPL_HEADER
#endif /* CYHAL_DMA_IMPL_HEADER */

/** \} group_hal_dma */