AM620-Q1: Best practices for audio processing design (McASP + tasks) in FreeRTOS SMP on AM62x A53

Part Number: AM620-Q1
Other Parts Discussed in Thread: SYSCONFIG

Hi Team,

With MCU+ SDK 11.02.00, FreeRTOS SMP is now supported on AM62x A53.

However, I am not sure how to properly design audio processing in this SMP environment, especially regarding task allocation and McASP handling.

If implemented without careful consideration, I believe there is a risk of introducing additional latency due to task migration across cores.

My current understanding is that:
- Audio processing tasks should be pinned to a specific core
- McASP interrupt/callback handling should also be fixed to a specific core
to ensure deterministic real-time behavior

I would like to ask:
What is the recommended best practice for implementing audio processing on FreeRTOS SMP?

Specifically, I am looking for guidance on:
- How to structure tasks (core affinity, prioritization, etc.)
- How to handle McASP interrupts/callbacks in an SMP environment
- Any required considerations for SysConfig settings
- Linker configuration (e.g., memory placement, non-cache buffers for DMA)

I am trying to understand this based on the following examples:
- hello_world SMP example:
  C:\ti\mcu_plus_sdk_am62x_12_00_00_27\examples\hello_world\am62x-sk\a53ss0-0_freertos-smp
- McASP playback example:
  C:\ti\mcu_plus_sdk_am62x_12_00_00_27\examples\drivers\mcasp\mcasp_playback_codec_aic31

And I have also reviewed this document:
- SMP FreeRTOS Guide:
  C:\ti\mcu_plus_sdk_am62x_12_00_00_27\docs\api_guide_am62x\SMP_FREERTOS_GUIDE.html

Additionally, in my use case, I need to use more than three cores.
Because of mailbox limitations, an AMP-based approach is not feasible.

I would appreciate if you could provide concrete guidance or recommended design patterns for this kind of use case.

Also, if there are any known limitations or constraints when applying SMP to real-time audio use cases, I would like to understand them.

Best regards,
Satoshi
  • Hi Satoshi,

    Thank you for your query. I will check with the team internally about the plans for creating this document or if some document is already present. Kindly expect a reply in a couple of days. 

    Regards,
    Ritapravo

  • Hi Ritapravo,

    Thanks for checking.
    I'll wait for your update .

    Best Regards,
    Satoshi

  • Hi Ritapravo,

    Additionally, I am aware of the following example as well, although I understand it is primarily for reference:

    Best regards,
    Satoshi

  • Could you please provide an update on this?
  • Hi Satoshi-san,

    Thank you for your patience and apologies for the delay. We are actively working on the SMP guide creation and is going through a series of checks before being available. Please wait for 2/3 days before we can provide it to you. 

    Regards,
    Ritapravo

  • Hi Satoshi-san,

    Please review the attached SMP guide. We hope it addresses all your queries comprehensively.

    Please let us know if you require any additional assistance.

    FREERTOS_SMP_AUDIO.pdf

    Regards,

    Anil.

  • Hi Anil-san,

    Thank you for sharing the SMP guide. It is very helpful.

    I have a couple of follow-up questions after reviewing the document.

    First, I would like to confirm on which A53 core the McASP interrupt and its completion callback are executed.
    Even if the McASP initialization is performed on Core 0, is there still a possibility that the interrupt/callback could be handled on other cores?

    Secondly, I would like to clarify a concern regarding core placement.

    If the callback is executed on a different core (e.g., Core 1), while the audio processing task is pinned to Core 0, it seems that buffer/data transfer across cores would be required.

    In such a case, I assume that additional latency may be introduced due to cross-core communication.
    Based on this, I am assuming that it may be necessary to place both the interrupt/callback handling and the audio processing tasks on the same core.

    Could you please confirm whether this understanding is correct, and if there is any recommended approach for handling this in FreeRTOS SMP?

    Regards,
    Satoshi

  • Hi Satoshi-san,

    In the MCU+ SDK, A53 interrupts are not shareable between the A53 cores.
    Consequently, all interrupts are routed exclusively to A53 core 0.
    For your specific use case, please initialize all McASP-related tasks on core 0.
    The McASP initialization task runs on A53 core 0, and the McASP interrupt must also be routed to A53 core 0 .
    The McASP interrupts cannot be handled by any other core.

    Regards,

    Anil.

  • Hi Anil-san,

    Thank you for the clear explanation. I understand that all interrupts are routed to A53 Core 0 in the MCU+ SDK.

    I have one additional question regarding the boot flow.

    In order to flash boot our existing AMP-based software, we found that some additional modifications were required beyond what is described in the provided PDF.

    Specifically, by modifying the makefile_ccs_bootimage_gen, we were able to make the SBL recognize the A53 application as an SMP configuration.

    Is this modification approach considered the recommended way?

    Or is there a more appropriate or standard method to handle SMP boot image generation on A53?

    Best regards, 

    Satoshi

  • Please refer to the attached file for the details of the differences. Due to file type restrictions, it is provided as a .txt file.

    #
    # Auto generated makefile
    #
    
    # Below variables need to be defined outside this file or via command line
    # - MCU_PLUS_SDK_PATH
    # - PROFILE
    # - CG_TOOL_ROOT
    # - OUTNAME
    # - CCS_INSTALL_DIR
    # - CCS_IDE_MODE
    
    CCS_PATH=$(CCS_INSTALL_DIR)
    include $(MCU_PLUS_SDK_PATH)/imports.mak
    include $(MCU_PLUS_SDK_PATH)/devconfig/devconfig.mak
    
    LD=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-ld
    STRIP=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-strip
    OBJCOPY=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-objcopy
    ifeq ($(OS), Windows_NT)
    	PYTHON=python
    else
    	PYTHON=python3
    endif
    
    OUTFILE=$(PROFILE)/$(OUTNAME).out
    BOOTIMAGE_PATH=$(abspath ${PROFILE})
    BOOTIMAGE_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage
    BOOTIMAGE_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage_xip
    BOOTIMAGE_NAME_SIGNED:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage.signed
    BOOTIMAGE_RPRC_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc
    BOOTIMAGE_RPRC_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_xip
    BOOTIMAGE_RPRC_NAME_TMP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_tmp
    
    #
    # Generation of boot image which can be loaded by Secondary Boot Loader (SBL)
    #
    ifeq ($(OS),Windows_NT)
    EXE_EXT=.exe
    endif
    ifeq ($(OS),Windows_NT)
      BOOTIMAGE_CERT_GEN_CMD=powershell -executionpolicy unrestricted -command $(MCU_PLUS_SDK_PATH)/tools/boot/signing/x509CertificateGen.ps1
    else
      BOOTIMAGE_CERT_GEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/signing/x509CertificateGen.sh
    endif
    BOOTIMAGE_TEMP_OUT_FILE=$(PROFILE)/temp_stdout_$(PROFILE).txt
    
    BOOTIMAGE_CORE_ID_a53ss0-0 = 0
    BOOTIMAGE_CORE_ID_a53ss0-1 = 1
    BOOTIMAGE_CORE_ID_a53ss1-0 = 2
    BOOTIMAGE_CORE_ID_a53ss1-1 = 3
    BOOTIMAGE_CORE_ID_r5fss0-0 = 4
    BOOTIMAGE_CORE_ID_m4fss0-0 = 5
    BOOTIMAGE_CORE_ID_hsm-m4fss0-0 = 6
    BOOTIMAGE_CORE_ID_load_only = 21
    SBL_RUN_ADDRESS=0x43C00000
    SBL_DEV_ID=55
    SW_VERSION=0x00000000
    
    MULTI_CORE_IMAGE_GEN = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/multicoreImageGen/multicoreImageGen.js
    OUTRPRC_CMD = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/out2rprc/elf2rprc.js
    APP_IMAGE_SIGN_CMD = $(MCU_PLUS_SDK_PATH)/tools/boot/signing/appimage_x509_cert_gen.py
    
    ifeq ($(OS),Windows_NT)
      XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.exe
    else
      XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out
    endif
    
    MULTI_CORE_IMAGE_PARAMS = \
    	$(BOOTIMAGE_RPRC_NAME)@$(BOOTIMAGE_CORE_ID_a53ss0-0) \
    
    
    MULTI_CORE_IMAGE_PARAMS_XIP = \
    	$(BOOTIMAGE_RPRC_NAME_XIP)@$(BOOTIMAGE_CORE_ID_a53ss0-0)  \
    
    
    all:
    ifeq ($(CCS_IDE_MODE),cloud)
    #	No post build steps
    else
    	@echo  Boot image: am62x:a53ss0-0:freertos:gcc-aarch64 $(BOOTIMAGE_NAME)  ...
    	$(OUTRPRC_CMD) $(OUTFILE) $(SW_VERSION)>> $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(COPY) $(OUTNAME).rprc $(BOOTIMAGE_RPRC_NAME)
    	$(COPY) $(BOOTIMAGE_RPRC_NAME) $(BOOTIMAGE_RPRC_NAME_TMP)
    	$(RM) $(BOOTIMAGE_RPRC_NAME)
    	$(XIPGEN_CMD) -i $(BOOTIMAGE_RPRC_NAME_TMP) -o $(BOOTIMAGE_RPRC_NAME) -x $(BOOTIMAGE_RPRC_NAME_XIP) --flash-start-addr 0x60000000 -v > $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME) $(MULTI_CORE_IMAGE_PARAMS) >> $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME_XIP) $(MULTI_CORE_IMAGE_PARAMS_XIP) >> $(BOOTIMAGE_TEMP_OUT_FILE)
    # Sign the appimage for HS-FS using appimage signing script
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs_fs --keyversion $(VERSION)
    ifeq ($(DEVICE_TYPE),HS)
    # Sign the appimage using appimage signing script
    ifeq ($(ENC_ENABLED),no)
    	@echo Boot image signing: Encryption is disabled.
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs --keyversion $(VERSION)
    else
    	@echo Boot image signing: Encryption is enabled.
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --enc y --enckey $(APP_ENCRYPTION_KEY) --output $(BOOTIMAGE_NAME).hs --keyversion $(VERSION)
    	$(RM) $(BOOTIMAGE_NAME)-enc
    endif
    endif
    	$(RM) $(BOOTIMAGE_RPRC_NAME_TMP)
    	@echo  Boot image: am62x:a53ss0-0:freertos:gcc-aarch64 $(BOOTIMAGE_NAME) Done !!!
    	@echo  .
    ifeq ($(DEVICE_TYPE),HS)
    	@echo  Boot image: am62x:a53ss0-0:freertos:gcc-aarch64 $(BOOTIMAGE_NAME).hs Done !!!
    	@echo  .
    else
    	@echo  Boot image: am62x:a53ss0-0:freertos:gcc-aarch64 $(BOOTIMAGE_NAME).hs_fs Done !!!
    	@echo  .
    endif
    endif
    
    #
    # Auto generated makefile
    #
    
    # Below variables need to be defined outside this file or via command line
    # - MCU_PLUS_SDK_PATH
    # - PROFILE
    # - CG_TOOL_ROOT
    # - OUTNAME
    # - CCS_INSTALL_DIR
    # - CCS_IDE_MODE
    
    CCS_PATH=$(CCS_INSTALL_DIR)
    include $(MCU_PLUS_SDK_PATH)/imports.mak
    include $(MCU_PLUS_SDK_PATH)/devconfig/devconfig.mak
    
    LD=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-ld
    STRIP=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-strip
    OBJCOPY=$(CG_TOOL_ROOT)/bin/aarch64-none-elf-objcopy
    ifeq ($(OS), Windows_NT)
    	PYTHON=python
    else
    	PYTHON=python3
    endif
    
    OUTFILE=$(PROFILE)/$(OUTNAME).out
    BOOTIMAGE_PATH=$(abspath ${PROFILE})
    BOOTIMAGE_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage
    BOOTIMAGE_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage_xip
    BOOTIMAGE_NAME_SIGNED:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage.signed
    BOOTIMAGE_RPRC_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc
    BOOTIMAGE_RPRC_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_xip
    BOOTIMAGE_RPRC_NAME_TMP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_tmp
    
    #
    # Generation of boot image which can be loaded by Secondary Boot Loader (SBL)
    #
    ifeq ($(OS),Windows_NT)
    EXE_EXT=.exe
    endif
    ifeq ($(OS),Windows_NT)
      BOOTIMAGE_CERT_GEN_CMD=powershell -executionpolicy unrestricted -command $(MCU_PLUS_SDK_PATH)/tools/boot/signing/x509CertificateGen.ps1
    else
      BOOTIMAGE_CERT_GEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/signing/x509CertificateGen.sh
    endif
    BOOTIMAGE_TEMP_OUT_FILE=$(PROFILE)/temp_stdout_$(PROFILE).txt
    
    BOOTIMAGE_CORE_ID_a53ss0-0 = 0
    BOOTIMAGE_CORE_ID_a53ss0-1 = 1
    BOOTIMAGE_CORE_ID_a53ss1-0 = 2
    BOOTIMAGE_CORE_ID_a53ss1-1 = 3
    BOOTIMAGE_CORE_ID_r5fss0-0 = 4
    BOOTIMAGE_CORE_ID_m4fss0-0 = 5
    BOOTIMAGE_CORE_ID_hsm-m4fss0-0 = 6
    BOOTIMAGE_CORE_ID_load_only = 21
    BOOTIMAGE_CORE_ID_freertos-smp = 100
    SBL_RUN_ADDRESS=0x43C00000
    SBL_DEV_ID=55
    SW_VERSION=0x00000000
    
    MULTI_CORE_IMAGE_GEN = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/multicoreImageGen/multicoreImageGen.js
    OUTRPRC_CMD = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/out2rprc/elf2rprc.js
    APP_IMAGE_SIGN_CMD = $(MCU_PLUS_SDK_PATH)/tools/boot/signing/appimage_x509_cert_gen.py
    
    ifeq ($(OS),Windows_NT)
      XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.exe
    else
      XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out
    endif
    
    MULTI_CORE_IMAGE_PARAMS = \
    	    $(BOOTIMAGE_RPRC_NAME)@$(BOOTIMAGE_CORE_ID_freertos-smp) \
    
    
    MULTI_CORE_IMAGE_PARAMS_XIP = \
    	    $(BOOTIMAGE_RPRC_NAME_XIP)@$(BOOTIMAGE_CORE_ID_freertos-smp) \
    
    
    all:
    ifeq ($(CCS_IDE_MODE),cloud)
    #	No post build steps
    else
    	@echo  Boot image: am62x:a53ss0-0:freertos-smp:gcc-aarch64 $(BOOTIMAGE_NAME)  ...
    	$(OUTRPRC_CMD) $(OUTFILE) $(SW_VERSION)>> $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(COPY) $(OUTNAME).rprc $(BOOTIMAGE_RPRC_NAME)
    	$(COPY) $(BOOTIMAGE_RPRC_NAME) $(BOOTIMAGE_RPRC_NAME_TMP)
    	$(RM) $(BOOTIMAGE_RPRC_NAME)
    	$(XIPGEN_CMD) -i $(BOOTIMAGE_RPRC_NAME_TMP) -o $(BOOTIMAGE_RPRC_NAME) -x $(BOOTIMAGE_RPRC_NAME_XIP) --flash-start-addr 0x60000000 -v > $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME) $(MULTI_CORE_IMAGE_PARAMS) >> $(BOOTIMAGE_TEMP_OUT_FILE)
    	$(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME_XIP) $(MULTI_CORE_IMAGE_PARAMS_XIP) >> $(BOOTIMAGE_TEMP_OUT_FILE)
    # Sign the appimage for HS-FS using appimage signing script
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs_fs --keyversion $(VERSION)
    ifeq ($(DEVICE_TYPE),HS)
    # Sign the appimage using appimage signing script
    ifeq ($(ENC_ENABLED),no)
    	@echo Boot image signing: Encryption is disabled.
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs --keyversion $(VERSION)
    else
    	@echo Boot image signing: Encryption is enabled.
    	$(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 0  --loadaddr 8A000000 --key $(APP_SIGNING_KEY) --enc y --enckey $(APP_ENCRYPTION_KEY) --output $(BOOTIMAGE_NAME).hs --keyversion $(VERSION)
    	$(RM) $(BOOTIMAGE_NAME)-enc
    endif
    endif
    	$(RM) $(BOOTIMAGE_RPRC_NAME_TMP)
    	@echo  Boot image: am62x:a53ss0-0:freertos-smp:gcc-aarch64 $(BOOTIMAGE_NAME) Done !!!
    	@echo  .
    ifeq ($(DEVICE_TYPE),HS)
    	@echo  Boot image: am62x:a53ss0-0:freertos-smp:gcc-aarch64 $(BOOTIMAGE_NAME).hs Done !!!
    	@echo  .
    else
    	@echo  Boot image: am62x:a53ss0-0:freertos-smp:gcc-aarch64 $(BOOTIMAGE_NAME).hs_fs Done !!!
    	@echo  .
    endif
    endif
    

  • Hi Satoshi-san,

    I am routing your query to Boot expert to comment on the above your queries .

    Regards,

    Anil.

  • Is this modification approach considered the recommended way?

    Or is there a more appropriate or standard method to handle SMP boot image generation on A53?

    These modifications are required for SMP to work, We generally recommend using any SMP example provided in the SDK like hello world as a base and modify it to create your custom application.

  • Hi Meet-san,

    Understood. Thank you for the clarification.

    We'll proceed with this approach.

    Best regards,
    Satoshi

  • I have one additional question regarding FreeRTOS SMP behavior.
    During our evaluation of the SMP configuration, we observed that the maximum processing load that Core0 can handle appears to depend on the load running on the other cores.
    For example:
    - When Core1–Core3 have no load, Core0 can handle a load level of approximately 10000 without any audio waveform corruption.
    - However, when Core1–Core3 are each running a load level of approximately 10000, audio waveform corruption occurs unless the load on Core0 is reduced to around 2000.
    From these results, it appears that the amount of processing that Core0 can sustain is affected by the load running on the other cores, even though the workload assigned to Core0 itself remains unchanged.
    My understanding was that each core should be able to execute its workload largely independently, so I was surprised to observe such a large difference.
    Is this expected behavior in a FreeRTOS SMP environment?
    If so, could you please explain what factors may contribute to this behavior?
    For example, could it be related to scheduler overhead, shared cache resources, DDR memory bandwidth contention, or other SMP-related mechanisms?
    Note:
    The values above are provided only as representative examples. In the actual evaluation, the processing load is controlled using an Audio Weaver load module, where the load is adjusted by changing the number of taps.
  • Hi,

    This bottleneck seems to be due to all A53 cores sharing a single L2 cache and is expected. All cores directly compete for the same L2 cache space, When Core1-Core3 are idle, Core0 has near-exclusive L2 access for your audio workload once you overload all cores this becomes an issue with all cores accessing the same L2 cache space, this may also lead to DDR Memory Bandwidth contention because L2 misses go directly to DDR.

    Best Regards,
    Meet.