LP-AM261: MCSPI Loopback example with DMA not working

Part Number: LP-AM261

Dear experts,

I am working on LP-AM261, with CCS 20 and SDK 11-01-00-19.

The examples of MCSPI Loopback examples seem broken on my evaluation board: i always get data mismatch. I tried both nortos and freertos. I also tried lowering the MCSPI Clock frequency but without any success.

Any help?

 

  • Hi Elia,

    Before we can pinpoint the root cause, a few details would help:

    • What does the data mismatch look like? Are you seeing complete corruption (all zeros or random data in RX), bit errors, or an offset/shifted pattern? This distinction matters for diagnosing whether it's a DMA transfer issue, cache coherency problem, or configuration error.
    • Does the non-DMA MCSPI loopback example work? (i.e., the polling/interrupt-based version without DMA). If that passes, the issue is isolated to DMA handling specifically.
    • Which LP-AM261 board revision are you using? SDK 10.02.00 documentation notes that LP-AM261 Rev. E2 is the supported revision [1].

    The MCSPI loopback DMA examples (both HLD and LLD variants) are designed to use internal pad-level loopback — D0 is configured as both TX Enable and RX input via SYSCFG — and should print "All tests have passed!!" on success [2][3]. The expected output confirms this is a self-contained test with no external wiring required.

    Troubleshooting Recommendations

    1. Test the non-DMA loopback example first — This isolates whether the MCSPI peripheral itself is working correctly in loopback mode, or if the issue is DMA-specific.

    2. Check cache coherency — DMA-based transfers on R5F cores require that TX/RX buffers are either placed in non-cacheable memory regions or that proper cache maintenance (writeback before TX, invalidate before reading RX) is performed. If the example's linker/MPU configuration doesn't match your build, stale cache data could cause mismatches.

    3. Verify SYSCFG pin configuration — Ensure the D0 pin is configured for both TX Enable and RX input as required for internal pad-level loopback [2].

    4. Check DMA buffer alignment — DMA transfers typically require buffers aligned to cache line boundaries (32 or 64 bytes depending on configuration).

    To help refine this recommendation, it would be helpful to know:

    • Whether this issue is tied to specific MCSPI instances (MCSPI0 vs others) or DMA controller channels
    • Specific SYSCFG or MPU/cache settings used for DMA buffers on LP-AM261s

    Given that the issue reproduces across both NORTOS and FreeRTOS and persists regardless of clock frequency, cache coherency or a DMA buffer placement issue is the most likely culprit — these would be OS-independent and clock-independent. I'd recommend starting with the non-DMA loopback test to confirm the peripheral works, then examining the memory map and MPU settings for the DMA buffers.

    If you can share the console output or describe the mismatch pattern (e.g., RX buffer all zeros vs. garbled data), that would significantly narrow the diagnosis.


    Resources:

    1. Motor Control SDK AM261x Release Notes 10.02.00
    2. MCSPI Loopback DMA Example Documentation
    3. MCSPI Loopback DMA LLD Example Documentation

    Best Regards,

    Zackary Fleenor

  • Hi Fleenor,

    • I am using Rev A of LP (there is a paper on the board with number PROC193A (001)
    • I also try just loopback example (without DMA), and this is not working
    • To highllight, I did not touch nothing of the example, just importing it and flash it to the board. This is my syscfg file:
      /**
       * These arguments were used when this file was generated. They will be automatically applied on subsequent loads
       * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
       * @cliArgs --device "AM261x_ZFG" --part "AM2612" --package "ZFG" --context "r5fss0-0" --product "MCU_PLUS_SDK_AM261x@11.01.00"
       * @v2CliArgs --device "AM2612" --package "NFBGA (ZFG)" --variant "500MHz" --context "r5fss0-0" --product "MCU_PLUS_SDK_AM261x@11.01.00"
       * @versions {"tool":"1.26.0+4407"}
       */
      
      /**
       * Import the modules used in this configuration.
       */
      const mcspi           = scripting.addModule("/drivers/mcspi/mcspi", {}, false);
      const mcspi1          = mcspi.addInstance();
      const debug_log       = scripting.addModule("/kernel/dpl/debug_log");
      const dpl_cfg         = scripting.addModule("/kernel/dpl/dpl_cfg");
      const mpu_armv7       = scripting.addModule("/kernel/dpl/mpu_armv7", {}, false);
      const mpu_armv71      = mpu_armv7.addInstance();
      const mpu_armv72      = mpu_armv7.addInstance();
      const mpu_armv73      = mpu_armv7.addInstance();
      const mpu_armv74      = mpu_armv7.addInstance();
      const mpu_armv75      = mpu_armv7.addInstance();
      const mpu_armv76      = mpu_armv7.addInstance();
      const mpu_armv77      = mpu_armv7.addInstance();
      const default_linker  = scripting.addModule("/memory_configurator/default_linker", {}, false);
      const default_linker1 = default_linker.addInstance();
      const general         = scripting.addModule("/memory_configurator/general", {}, false);
      const general1        = general.addInstance();
      const region          = scripting.addModule("/memory_configurator/region", {}, false);
      const region1         = region.addInstance();
      const section         = scripting.addModule("/memory_configurator/section", {}, false);
      const section1        = section.addInstance();
      const section2        = section.addInstance();
      const section3        = section.addInstance();
      const section4        = section.addInstance();
      const section5        = section.addInstance();
      const section6        = section.addInstance();
      const section7        = section.addInstance();
      const section8        = section.addInstance();
      const section9        = section.addInstance();
      const section10       = section.addInstance();
      const section11       = section.addInstance();
      const section12       = section.addInstance();
      
      /**
       * Write custom configuration values to the imported modules.
       */
      mcspi1.$name                       = "CONFIG_MCSPI0";
      mcspi1.inputSelect                 = "0";
      mcspi1.SPI.$assign                 = "SPI0";
      mcspi1.SPI.CLK.$assign             = "GPIO12";
      mcspi1.SPI.D0.$assign              = "GPIO13";
      mcspi1.SPI.D1.$assign              = "GPIO14";
      mcspi1.mcspiChannel[0].$name       = "CONFIG_MCSPI_CH0";
      mcspi1.mcspiChannel[0].bitRate     = 50000000;
      mcspi1.mcspiChannel[0].CSn.$assign = "GPIO11";
      mcspi1.child.$name                 = "drivers_mcspi_v1_mcspi_v1_template0";
      
      const edma                  = scripting.addModule("/drivers/edma/edma", {}, false);
      const edma1                 = edma.addInstance({}, false);
      edma1.$name                 = "CONFIG_EDMA0";
      mcspi1.edmaDriver           = edma1;
      edma1.edmaRmDmaCh[0].$name  = "CONFIG_EDMA_RM0";
      edma1.edmaRmQdmaCh[0].$name = "CONFIG_EDMA_RM1";
      edma1.edmaRmTcc[0].$name    = "CONFIG_EDMA_RM2";
      edma1.edmaRmParam[0].$name  = "CONFIG_EDMA_RM3";
      
      debug_log.enableUartLog            = true;
      debug_log.uartLog.$name            = "CONFIG_UART_CONSOLE";
      debug_log.uartLog.UART.$assign     = "UART0";
      debug_log.uartLog.UART.RXD.$assign = "GPIO27";
      debug_log.uartLog.UART.TXD.$assign = "GPIO28";
      debug_log.uartLog.child.$name      = "drivers_uart_v2_uart_v2_template0";
      
      mpu_armv71.$name             = "CONFIG_MPU_REGION0";
      mpu_armv71.size              = 31;
      mpu_armv71.attributes        = "Device";
      mpu_armv71.accessPermissions = "Supervisor RD+WR, User RD";
      mpu_armv71.allowExecute      = false;
      
      mpu_armv72.$name             = "CONFIG_MPU_REGION1";
      mpu_armv72.size              = 15;
      mpu_armv72.accessPermissions = "Supervisor RD+WR, User RD";
      
      mpu_armv73.$name             = "CONFIG_MPU_REGION2";
      mpu_armv73.baseAddr          = 0x41010000;
      mpu_armv73.size              = 15;
      mpu_armv73.accessPermissions = "Supervisor RD+WR, User RD";
      
      mpu_armv74.$name             = "CONFIG_MPU_REGION3";
      mpu_armv74.baseAddr          = 0x70000000;
      mpu_armv74.size              = 21;
      mpu_armv74.accessPermissions = "Supervisor RD+WR, User RD";
      
      mpu_armv75.$name             = "CONFIG_MPU_REGION4";
      mpu_armv75.baseAddr          = 0x60000000;
      mpu_armv75.size              = 28;
      mpu_armv75.accessPermissions = "Supervisor RD, User RD";
      
      mpu_armv76.$name       = "CONFIG_MPU_REGION5";
      mpu_armv76.baseAddr    = 0x80000000;
      mpu_armv76.size        = 31;
      mpu_armv76.tex         = 0;
      mpu_armv76.isCacheable = false;
      
      mpu_armv77.$name       = "CONFIG_MPU_REGION6";
      mpu_armv77.baseAddr    = 0x52200000;
      mpu_armv77.size        = 15;
      mpu_armv77.attributes  = "CUSTOM";
      mpu_armv77.tex         = 0;
      mpu_armv77.isCacheable = false;
      
      default_linker1.$name = "memory_configurator_default_linker0";
      
      general1.$name        = "CONFIG_GENERAL0";
      general1.linker.$name = "TIARMCLANG0";
      
      region1.$name                                = "MEMORY_REGION_CONFIGURATION0";
      region1.memory_region.create(11);
      region1.memory_region[0].type                = "TCMA";
      region1.memory_region[0].$name               = "R5F_VECS";
      region1.memory_region[0].size                = 0x40;
      region1.memory_region[0].auto                = false;
      region1.memory_region[1].type                = "TCMA";
      region1.memory_region[1].$name               = "R5F_TCMA";
      region1.memory_region[1].size                = 0x7FC0;
      region1.memory_region[2].type                = "TCMB";
      region1.memory_region[2].size                = 0x8000;
      region1.memory_region[2].$name               = "R5F_TCMB";
      region1.memory_region[3].$name               = "SBL";
      region1.memory_region[3].auto                = false;
      region1.memory_region[3].size                = 0x40000;
      region1.memory_region[4].$name               = "OCRAM";
      region1.memory_region[4].auto                = false;
      region1.memory_region[4].manualStartAddress  = 0x70040000;
      region1.memory_region[4].size                = 0x40000;
      region1.memory_region[5].type                = "FLASH";
      region1.memory_region[5].auto                = false;
      region1.memory_region[5].manualStartAddress  = 0x60100000;
      region1.memory_region[5].size                = 0x80000;
      region1.memory_region[5].$name               = "FLASH";
      region1.memory_region[6].$name               = "USER_SHM_MEM";
      region1.memory_region[6].auto                = false;
      region1.memory_region[6].manualStartAddress  = 0x70150000;
      region1.memory_region[6].size                = 0x4000;
      region1.memory_region[6].isShared            = true;
      region1.memory_region[6].shared_cores        = ["r5fss0-1"];
      region1.memory_region[7].$name               = "LOG_SHM_MEM";
      region1.memory_region[7].auto                = false;
      region1.memory_region[7].manualStartAddress  = 0x70154000;
      region1.memory_region[7].size                = 0x4000;
      region1.memory_region[7].isShared            = true;
      region1.memory_region[7].shared_cores        = ["r5fss0-1"];
      region1.memory_region[8].type                = "CUSTOM";
      region1.memory_region[8].$name               = "RTOS_NORTOS_IPC_SHM_MEM";
      region1.memory_region[8].auto                = false;
      region1.memory_region[8].manualStartAddress  = 0x72000000;
      region1.memory_region[8].size                = 0x3E80;
      region1.memory_region[8].isShared            = true;
      region1.memory_region[8].shared_cores        = ["r5fss0-1"];
      region1.memory_region[9].type                = "CUSTOM";
      region1.memory_region[9].$name               = "MAILBOX_HSM";
      region1.memory_region[9].auto                = false;
      region1.memory_region[9].manualStartAddress  = 0x44000000;
      region1.memory_region[9].size                = 0x3CE;
      region1.memory_region[9].isShared            = true;
      region1.memory_region[9].shared_cores        = ["r5fss0-1"];
      region1.memory_region[10].type               = "CUSTOM";
      region1.memory_region[10].$name              = "MAILBOX_R5F";
      region1.memory_region[10].auto               = false;
      region1.memory_region[10].manualStartAddress = 0x44000400;
      region1.memory_region[10].size               = 0x3CE;
      region1.memory_region[10].isShared           = true;
      region1.memory_region[10].shared_cores       = ["r5fss0-1"];
      
      section1.load_memory                  = "R5F_VECS";
      section1.group                        = false;
      section1.$name                        = "Vector Table";
      section1.output_section.create(1);
      section1.output_section[0].$name      = ".vectors";
      section1.output_section[0].palignment = true;
      
      section2.load_memory                  = "OCRAM";
      section2.$name                        = "Text Segments";
      section2.output_section.create(5);
      section2.output_section[0].$name      = ".text.hwi";
      section2.output_section[0].palignment = true;
      section2.output_section[1].$name      = ".text.cache";
      section2.output_section[1].palignment = true;
      section2.output_section[2].$name      = ".text.mpu";
      section2.output_section[2].palignment = true;
      section2.output_section[3].$name      = ".text.boot";
      section2.output_section[3].palignment = true;
      section2.output_section[4].$name      = ".text:abort";
      section2.output_section[4].palignment = true;
      
      section3.load_memory                  = "OCRAM";
      section3.$name                        = "Code and Read-Only Data";
      section3.output_section.create(2);
      section3.output_section[0].$name      = ".text";
      section3.output_section[0].palignment = true;
      section3.output_section[1].$name      = ".rodata";
      section3.output_section[1].palignment = true;
      
      section4.load_memory                  = "OCRAM";
      section4.$name                        = "Data Segment";
      section4.output_section.create(1);
      section4.output_section[0].$name      = ".data";
      section4.output_section[0].palignment = true;
      
      section5.load_memory                             = "OCRAM";
      section5.$name                                   = "Memory Segments";
      section5.output_section.create(3);
      section5.output_section[0].$name                 = ".bss";
      section5.output_section[0].output_sections_start = "__BSS_START";
      section5.output_section[0].output_sections_end   = "__BSS_END";
      section5.output_section[0].palignment            = true;
      section5.output_section[1].$name                 = ".sysmem";
      section5.output_section[1].palignment            = true;
      section5.output_section[2].$name                 = ".stack";
      section5.output_section[2].palignment            = true;
      
      section6.load_memory                              = "OCRAM";
      section6.$name                                    = "Stack Segments";
      section6.output_section.create(5);
      section6.output_section[0].$name                  = ".irqstack";
      section6.output_section[0].output_sections_start  = "__IRQ_STACK_START";
      section6.output_section[0].output_sections_end    = "__IRQ_STACK_END";
      section6.output_section[0].input_section.create(1);
      section6.output_section[0].input_section[0].$name = ". = . + __IRQ_STACK_SIZE;";
      section6.output_section[1].$name                  = ".fiqstack";
      section6.output_section[1].output_sections_start  = "__FIQ_STACK_START";
      section6.output_section[1].output_sections_end    = "__FIQ_STACK_END";
      section6.output_section[1].input_section.create(1);
      section6.output_section[1].input_section[0].$name = ". = . + __FIQ_STACK_SIZE;";
      section6.output_section[2].$name                  = ".svcstack";
      section6.output_section[2].output_sections_start  = "__SVC_STACK_START";
      section6.output_section[2].output_sections_end    = "__SVC_STACK_END";
      section6.output_section[2].input_section.create(1);
      section6.output_section[2].input_section[0].$name = ". = . + __SVC_STACK_SIZE;";
      section6.output_section[3].$name                  = ".abortstack";
      section6.output_section[3].output_sections_start  = "__ABORT_STACK_START";
      section6.output_section[3].output_sections_end    = "__ABORT_STACK_END";
      section6.output_section[3].input_section.create(1);
      section6.output_section[3].input_section[0].$name = ". = . + __ABORT_STACK_SIZE;";
      section6.output_section[4].$name                  = ".undefinedstack";
      section6.output_section[4].output_sections_start  = "__UNDEFINED_STACK_START";
      section6.output_section[4].output_sections_end    = "__UNDEFINED_STACK_END";
      section6.output_section[4].input_section.create(1);
      section6.output_section[4].input_section[0].$name = ". = . + __UNDEFINED_STACK_SIZE;";
      
      section7.load_memory                  = "OCRAM";
      section7.$name                        = "Initialization and Exception Handling";
      section7.output_section.create(3);
      section7.output_section[0].$name      = ".ARM.exidx";
      section7.output_section[0].palignment = true;
      section7.output_section[1].$name      = ".init_array";
      section7.output_section[1].palignment = true;
      section7.output_section[2].$name      = ".fini_array";
      section7.output_section[2].palignment = true;
      
      section8.load_memory                 = "USER_SHM_MEM";
      section8.type                        = "NOLOAD";
      section8.$name                       = "User Shared Memory";
      section8.group                       = false;
      section8.output_section.create(1);
      section8.output_section[0].$name     = ".bss.user_shared_mem";
      section8.output_section[0].alignment = 0;
      
      section9.load_memory                 = "LOG_SHM_MEM";
      section9.$name                       = "Log Shared Memory";
      section9.group                       = false;
      section9.type                        = "NOLOAD";
      section9.output_section.create(1);
      section9.output_section[0].$name     = ".bss.log_shared_mem";
      section9.output_section[0].alignment = 0;
      
      section10.load_memory                 = "RTOS_NORTOS_IPC_SHM_MEM";
      section10.type                        = "NOLOAD";
      section10.$name                       = "IPC Shared Memory";
      section10.group                       = false;
      section10.output_section.create(1);
      section10.output_section[0].$name     = ".bss.ipc_vring_mem";
      section10.output_section[0].alignment = 0;
      
      section11.load_memory                 = "MAILBOX_HSM";
      section11.type                        = "NOLOAD";
      section11.$name                       = "SIPC HSM Queue Memory";
      section11.group                       = false;
      section11.output_section.create(1);
      section11.output_section[0].$name     = ".bss.sipc_hsm_queue_mem";
      section11.output_section[0].alignment = 0;
      
      section12.load_memory                 = "MAILBOX_R5F";
      section12.$name                       = "SIPC R5F Queue Memory";
      section12.group                       = false;
      section12.type                        = "NOLOAD";
      section12.output_section.create(1);
      section12.output_section[0].$name     = ".bss.sipc_secure_host_queue_mem";
      section12.output_section[0].alignment = 0;
      
    • Data seems random. I send 128B and below my results:
      • When I use low frequency clock (1MHz)
        • tx = 0xAA, rx = 0xCC
        • tx = 0x55, rx = 0x33
        • tx = 0xFF, rx = 0xFF
    • Moreover, just after full power off and on, example seems to work

    My conclusion is that there is something weird with the board, as the example seem to work randomly (sometimes it works, sometimes it does not work).

    Any clue or something that I can try?

  • Hi Elia,

    Thank you for the additional details — this helps narrow down the issue significantly.

    Key Findings

    The fact that both the DMA and non-DMA loopback examples fail rules out DMA-specific issues (cache coherency, buffer alignment) and points to a more fundamental MCSPI or board-level problem.

    The intermittent behavior you're describing is particularly revealing:

    • Works immediately after full power cycle
    • Fails on subsequent runs
    • Data corruption with specific patterns (0xAA→0xCC, 0x55→0x33, 0xFF→0xFF)

    The bit patterns you're seeing suggest bit-level corruption:

    • 0xAA = 101010100xCC = 11001100
    • 0x55 = 010101010x33 = 00110011

    This alternating bit modification pattern, combined with the "works after power cycle" behavior, suggests the issue is related to peripheral state not being properly reset between runs.

    Critical: Board Revision

    You mentioned you have Rev A (PROC193A). The SDK documentation typically references Rev E2 as the supported revision for LP-AM261. This revision mismatch could be the root cause, as:

    • Internal loopback routing may differ between revisions
    • Pin configurations may have changed

    Recommended Troubleshooting Steps

    1. Verify SDK/Board Compatbility - I would recommend getting a RevE2 board.

    2. Test External Loopback
    Instead of relying on internal pad-level loopback:

    • Physically connect MCSPI TX (D0) to RX (D1) with a jumper wire on the board headers
    • Run the example again
    • If external loopback works but internal doesn't, this confirms a board/silicon-level internal loopback issue

    3. Try Different MCSPI Instances

    • Test with MCSPI1 or MCSPI2 (if available in the examples)
    • This helps determine if the issue is specific to MCSPI0 or affects all instances

    4. Add Explicit Module Reset
    Try adding a complete peripheral reset before initialization. You can modify the example to force a clean state:

    /* Before MCSPI_open() in your example */
    SOC_moduleClockDisable(SOC_RcmPeripheralId_MCSPI0, 0);
    ClockP_usleep(1000);
    SOC_moduleClockEnable(SOC_RcmPeripheralId_MCSPI0, 1);

    Next Steps

    Given that this is likely a board revision compatibility issue, I recommend:

    1. Confirm your SDK version officially supports Rev A hardware — if not, you may need either:

      • An older SDK version that targets Rev A
      • A Rev E2 board upgrade
    2. Test external loopback first — this is the quickest way to isolate whether it's an internal loopback routing issue vs. a general MCSPI problem

    3. If external loopback works, we know the MCSPI peripheral itself is functional and the issue is specific to internal loopback on Rev A

    Could you please try the external loopback test and let us know the results?

    Best Regards,

    Zackary Fleenor