Peripheral Drivers and Interrupts
This section provides information on the basics of peripheral drivers, interrupts, DMA, and how peripheral drivers work on HiFi4 DSP.
Basics
As both cores use shared SRAM and all digital peripherals, SDK drivers work the same on HiFi4 DSP. User Manual section 1.3 Block Diagram shows the system architect on this perspective. For applications, it is the same to use SDK drivers on HiFi4 DSP with Cortex M33 core. SDK has a DSP hello world UART example showing how easy to use UART on DSP side, same as Cortex M33 programming:
#include <xtensa/config/core.h>
#include "fsl_debug_console.h"
/* Init board hardware. */
BOARD_InitDebugConsole();
PRINTF("Hello World running on DSP core '%s', %s_%s\r\n", XCHAL_CORE_ID);
Compare with Hello world programming on Cortex M33 side. There are two major differences:
Pin initialization is not necessary for DSP. It is doable but it is highly recommended managing all pins from Cortex M33 side to make pins all-in-one place. It is also to avoid possible conflicts when you set pins on two different cores;
Clock setting is not necessary for DSP. It is also doable but same it is highly recommended managing all clock sources all-in-one place;
Parent topic:Peripheral Drivers and Interrupts
DMA
RT6xx has two DMA controllers. Each has the same DMA request and trigger input possibilities. The two intended scenarios for their use are:
One DMA controller (DMA0) is used by the CM33, the other (DMA1) is used by the HiFi4. This case can apply to systems where there is no need to differentiate security between the CPUs or between different tasks.
One DMA controller (DMA0) is secured and has access to secure spaces and peripherals. The other (DMA1) is not secured and does not have access to secure spaces and peripherals. In this scenario, only the secure code running on the CM33 has access to the secure DMA controller. The other code and the HiFi4 share the non-secure DMA controller (if needed).
HiFi4 DSP always uses DMA1 controller. Again, it is the same to call DMA drivers at HiFi4 DSP side. The DMA destination buffer and DMA descriptor have to be in non-cached area to ensure that each transaction flushes to memory immediately. The below example code showing how to create a DMIC DMA channel:
AT_NONCACHEABLE_SECTION_ALIGN(
static uint8_t s_buffer[BUFFER_SIZE * BUFFER_NUM], 4
);
AT_NONCACHEABLE_SECTION_ALIGN(
dma_descriptor_t s_dmaDescriptorPingpong[2], 16
);
#define DEMO_DMA (DMA1)
#define DEMO_DMIC_RX_CHANNEL DMAREQ_DMIC0
DMA_Init(DEMO_DMA);
DMA_EnableChannel(DEMO_DMA, DEMO_DMIC_RX_CHANNEL);
DMA_SetChannelPriority(DEMO_DMA, DEMO_DMIC_RX_CHANNEL, kDMA_ChannelPriority2);
DMA_CreateHandle(&s_dmicRxDmaHandle, DEMO_DMA, DEMO_DMIC_RX_CHANNEL);
Macro AT_NONCACHEABLE_SECTION_ALIGN
is defined in SDK driver layer and refer to non-cached area specified by memory map.
For more details about DMA, see the User Manual Chapter 11 RT6xx DMA Controller and SDK DMA examples in <SDK path>\boards\evkmimxrt685\driver_examples\dma
.
For audio peripherals/ DMIC/ I2S DMA, see <SDK path>\boards\evkmimxrt685\driver_examples\dmic
and <SDK path>\boards\evkmimxrt685\driver_examples\i2s
.
Parent topic:Peripheral Drivers and Interrupts
Interrupts
HiFi4 DSP has total 32 interrupts and 4 interrupt levels. Besides the first five interrupts, the rest interrupt 5 ~ 31 are multiplexed to allow more control over priorities and more general flexibility. To see the full list, see the User Manual section 8.6.3 DSP Interrupt Input Multiplexers.
It only requires few lines of code to enable an interrupt by calling XOS function calls, and peripheral controls are identical as Cortex M33 side. For example, the below code shows how to enable a UTick timer at DSP side:
First, include XOS system header files and libraries to enable necessary XOS functions.
To include libraries, go to Build Properties > Libraries > Add libraries > Select ‘xos’ & ‘xtutil’ and OK to accept.
#include <xtensa/config/core.h>
#include <xtensa/xos.h>
Set up the UTick callback and delay functions. This part is identical with Cortex M33 side. For Cortex M33 side implementation, see SDK path\\boards\evkmimxrt685\driver_examples\utick
#define UTICK_TIME_1S (1000000UL)
#define EXAMPLE_UTICK UTICK0
static volatile bool utickExpired;
static void UTickCallback(void)
{
utickExpired = true;
}
static void UTickDelay(uint32_t usec)
{
/* Set the UTICK timer to wake up the device from reduced power mode */
UTICK_SetTick(EXAMPLE_UTICK, kUTICK_Onetime, usec - 1, UTickCallback);
while (!utickExpired)
{
}
utickExpired = false;
}
Initialize the XOS and start UTick timer. The XOS function calls are the differences with Cortex M33 side:
/* Initialize XOS thread and start scheduler. Priority 7*/
xos_start_main("main", 7, 0);
/* Init board hardware. */
CLOCK_AttachClk(kLPOSC_to_UTICK_CLK);
CLOCK_EnableClock(kCLOCK_InputMux);
UTICK_Init(EXAMPLE_UTICK);
INPUTMUX_AttachSignal(INPUTMUX, 10U, kINPUTMUX_Utick0ToDspInterrupt);
/* To register interrupt callback */
xos_register_interrupt_handler(15, (XosIntFunc *)UTICK0_DriverIRQHandler, NULL);
/* To enable the interrupt */
xos_interrupt_enable(15);
while (1)
{
UTickDelay(UTICK_TIME_1S);
PRINTF("DSP UTICK TIMER every 1s\r\n");
}
Pay attention to the instant numbers. Pick interrupt 15, which maps to DSP_INT0_SEL10 as a L1 interrupts, lowest priority level. For more details about RT6xx HiFi4 DSP interrupt configuration, see User Manual section 51.7 Interrupt. Therefore, for Inputmux, attach UTick interrupt to 10. 15-5=10 as first five interrupts are reserved, not user configurable. To get it to highest priority level, for example L3, configure interrupt as follows.
INPUTMUX_AttachSignal(INPUTMUX, 24U,
kINPUTMUX_Utick0ToDspInterrupt);
xos_register_interrupt_handler(29, (XosIntFunc
*)UTICK0_DriverIRQHandler, NULL);
xos_interrupt_enable(29);
kINPUTMUX_Utick0ToDspInterrupt specifies DSP interrupt multiplexing value. It has been defined in SDK and matching User Manual section 8.6.3 DSP Interrupt Input Multiplexers.
For more details about XOS interrupt handling, see the Xtensa XOS Reference Manual Chapter 18 Interrupt and Exception Handling, and Chapter 26 Interrupt Handler Restrictions.
Parent topic:Peripheral Drivers and Interrupts
Complete Example
To present better how peripheral drivers work on HiFi4 DSP, below list a bare-metal DSP example program that transfers data from DMIC to codec on RT6xx EVKs. The full workspace located in <SDK path>\boards\evkmimxrt685\dsp_examples\audio_demo_bm
. This example is derived from Cortex M33 driver <SDK path>\boards\evkmimxrt685\driver_examples\dmic\dmic_i2s_dma
with below slightly modifications to adapt to HiFi4 DSP.
Move DMA buffer and descriptors into non-cached memory partitions;
Using DMA1 for DSP;
Calling XOS functions to enable interrupts.
This example does not contain any pin mux initializing or clock configurations. See the above Cortex M33 example dmic_i2s_dma to set up Cortex M33 side. Once Cortex M33 side ready, this example is compiled and run same as any other SDK DSP examples.
Connect headphone/earphone on audio out of the board, speak on DMIC, or play song nearby the DMIC, you can hear sound on the left channel of headphone/earphone.
/* Start XOS */
xos_start_main("main", 7, 0);
/* Disable DSP cache for noncacheable sections. DMA MUST run on none-cacheable/ cache bypass area*/
xthal_set_region_attribute((uint32_t *)&NonCacheable_start,
(uint32_t)&NonCacheable_end - (uint32_t)&NonCacheable_start, XCHAL_CA_BYPASS, 0);
xthal_set_region_attribute((uint32_t *)&NonCacheable_init_start,
(uint32_t)&NonCacheable_init_end - (uint32_t)&NonCacheable_init_start, XCHAL_CA_BYPASS,
0);
PRINTF("Configure DMA\r\n");
/* DSP_INT0_SEL18 = DMA1 */
INPUTMUX_AttachSignal(INPUTMUX, 18U, kINPUTMUX_Dmac1ToDspInterrupt);
/* Map DMA IRQ handler to INPUTMUX selection DSP_INT0_SEL18
* EXTINT19 = DSP INT 23 */
xos_register_interrupt_handler(XCHAL_EXTINT19_NUM, (XosIntFunc *)DMA_IRQHandle, DMA1);
xos_interrupt_enable(XCHAL_EXTINT19_NUM);
/* The rest DMA & DMIC operations are identical with CM33 side */
Parent topic:Peripheral Drivers and Interrupts
Parent topic:HiFi4 System Programming