This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

PROCESSOR-SDK-AM64X: AM64x MCSPI Slave with DMA for TX

Part Number: PROCESSOR-SDK-AM64X
Other Parts Discussed in Thread: SYSCONFIG

I tried to implement a SPI Slave with TX DMA, based on the example of AM65x "pdk_am65xx_08_00_00_36\packages\ti\drv\spi\soc\dma\v2\SPI_dma.c" for the AM64x. 

In AM64x SDK, UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX is provided as chPrms.peerChNum. With this configuration I get an DMA_COMPLETE Interrupt after SPI transfer, but no data is transfered to the MCSPI_TX_0 Register.

1.) Is it even possible to use the MCSPI TX DMA on the AM64x?

2.) Is it possible to use the TX DMA without using/enabling the send FIFO of the SPI? Goal is to load only the first 4 Bytes at start of transfer, to be able to write the source buffer in RAM right before the SPI access happens.

3.) Is the UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX  defined with 0xC300 in the headers is correct for the AM64x?

If all above is correct or possible:

4.) Is there an running AM64x_EVM example similar to the AM65x example: pdk_am65xx_08_00_00_36\packages\ti\drv\spi\example\mcspi_slavemode?

 

 

  • Hi Robert,

    First of all, AM64x and AM65x are different SoC. AM64x is using the MCU+ SDK which is different than the PDK used by AM65x. To get the latest MCU+ SDK, you can download it from https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/08_01_00_25/index_FDS.html

    What AM64x SDK which uses the "UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX as chPrms.peerChNum"? I did not see it in the MCU+ SDK

    1) Yes, it is possible to use the McSPI with TX DMA

    2) Yes, the FIFO threshold can be adjusted see details in 12.1.4.4.6 MCSPI FIFO Buffer Management in TRM (https://www.ti.com/lit/pdf/spruim2)

    3) It depends on where your SPI master is connected to, but CH0 is correct, because as slave mode, only ch0 can be used

    4) there are three McSPi examples in MCU+ SDK, none of them are slave mode, but you can change it in the example.syscfg (MCSPI --> Mode pf Operation).

    Best regards,

    Ming

  • Hi Ming,

    thank you for your reply.

    We are currenty using the AM64x MCU+ SDK 08.01.00. Running on FreeRTOS.

    I have already found the 3 examples, but none shows the use of the DMA, maybe because the provided driver does not support it yet.
    I got the SPI Slave already running using the FIFO, but required SPI communication speed is to high to be handled by interrupt,
    so in my opinion DMA is the only option to solve the problem.

    Therefore I tried to implement the DMA on my own and used the information out of the AM65x SDK to get a glue how to get the DMA running
    (AM64x is a compiler option in this example). The define UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX is provided in
    %TI_HOME_DIR%\mcu_plus_sdk_am64x\source\drivers\udma\soc\am64x_am243x\udma_soc.h and I use it for open the channel 0 of SPI1:

    Following Code is used. DMA_COMPLETE interrupt fires after SPI access, but without any data transfered from the buffer to the TX FIFO of the SPI.
    When I set the buffer to send, i execute a CacheP_wb to ensure changes are done in the physical RAM.
    Can you have a look on my dma initialization and give me a hint what I am doing wrong?


    /** \brief Number of ring entries - we can prime this much ADC operations */
    #define UDMA_TEST_RING_ENTRIES (1U)
    /** \brief Size (in bytes) of each ring entry (Size of pointer - 32-bit) */
    #define UDMA_TEST_RING_ENTRY_SIZE (sizeof(uint32_t))
    /** \brief Total ring memory */
    #define UDMA_TEST_RING_MEM_SIZE (UDMA_TEST_RING_ENTRIES * UDMA_TEST_RING_ENTRY_SIZE)

    static uint8_t gTxFqRingMem[UDMA_ALIGN_SIZE(UDMA_TEST_RING_MEM_SIZE)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    static uint8_t gTxCompRingMem[UDMA_ALIGN_SIZE(UDMA_TEST_RING_MEM_SIZE)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    static uint8_t gTxBuffer[128] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    static uint8_t udmaHpdMem[UDMA_ALIGN_SIZE(sizeof(CSL_UdmapCppi5HMPD))] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    static Udma_DrvHandle drvHandle = &gUdmaDrvObj[udmaIndex]; // provided by sysconfig generated sources
    static Udma_ChObject udmaChObj;
    static Udma_ChHandle chHandle = &udmaChObj;
    static Udma_EventObject udmaCqEventObj;


    void init()
    {

    /* Init channel parameters */
    uint32_t chType = UDMA_CH_TYPE_PDMA_TX;
    Udma_ChPrms chPrms;
    UdmaChPrms_init(&chPrms, chType);
    chPrms.peerChNum = UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX; // 0xC004
    chPrms.fqRingPrms.ringMem = &gTxFqRingMem[0U];
    chPrms.cqRingPrms.ringMem = &gTxCompRingMem[0U];
    chPrms.fqRingPrms.elemCnt = 1;
    chPrms.cqRingPrms.elemCnt = 1;
    retVal = Udma_chOpen(drvHandle, chHandle, chType, chPrms);

    Udma_ChTxPrms txPrmsLocal;
    UdmaChTxPrms_init(txPrmsLocal, chType);
    retVal = Udma_chConfigTx(chHandle, txPrms);

    /* Register ring completion callback */
    Udma_EventHandle eventHandle = &udmaCqEventObj;
    Udma_EventPrms eventPrms;
    UdmaEventPrms_init(&eventPrms);
    eventPrms.eventType = UDMA_EVENT_TYPE_DMA_COMPLETION;
    eventPrms.eventMode = UDMA_EVENT_MODE_EXCLUSIVE;
    eventPrms.chHandle = chHandle;
    eventPrms.masterEventHandle = NULL;
    eventPrms.eventCb = &bcdma_channel_udmaEventDmaCb;
    eventPrms.appData = this;
    retVal = Udma_eventRegister(drvHandle, eventHandle, &eventPrms);
    DebugP_assert(UDMA_SOK == retVal);

    }

    void startTxDmaTransfer(const uint8_t *buffer, uint32_t length)
    {

    uint32_t retVal = UDMA_SOK;
    if (UDMA_SOK == retVal)
    {

    Udma_ChPdmaPrms pdmaPrms;
    UdmaChPdmaPrms_init(&pdmaPrms);
    pdmaPrms.elemSize = UDMA_PDMA_ES_8BITS;
    pdmaPrms.elemCnt = LEVEL_TO_SIGNAL_TX_DATA; // 32 as set as AEL
    pdmaPrms.fifoCnt = 0;
    retVal = Udma_chConfigPdma(chHandle, pdmaPrms);

    }

    if (UDMA_SOK == retVal)
    {

    retVal = Udma_chEnable(chHandle);

    }
    if (UDMA_SOK == retVal)
    {

    /* Update host packet descriptor */
    updateHostPackageDescriptor(buffer, length);

    /* Submit HPD to channel */
    retVal = Udma_ringQueueRaw(Udma_chGetFqRingHandle(chHandle), (uint64_t) &udmaHpdMem);

    }

    }

    void updateHostPackageDescriptor(const uint8_t *buffer, uint32_t length)
    {

    CSL_UdmapCppi5HMPD *pHpd = (CSL_UdmapCppi5HMPD *) &udmaHpdMem;
    uint32_t descType = (uint32_t)CSL_UDMAP_CPPI5_PD_DESCINFO_DTYPE_VAL_HOST;
    uint32_t cqRingNum = Udma_chGetCqRingNum(chHandle);

    /* Setup descriptor */
    CSL_udmapCppi5SetDescType(pHpd, descType);
    CSL_udmapCppi5SetEpiDataPresent(pHpd, FALSE);
    CSL_udmapCppi5SetPsDataLoc(pHpd, 0U);
    CSL_udmapCppi5SetPsDataLen(pHpd, 0U);
    CSL_udmapCppi5SetPktLen(pHpd, descType, length);
    CSL_udmapCppi5SetPsFlags(pHpd, 0U);
    CSL_udmapCppi5SetIds(pHpd, descType, 0x321, UDMA_DEFAULT_FLOW_ID);
    CSL_udmapCppi5SetSrcTag(pHpd, 0x0000); /* Not used */
    CSL_udmapCppi5SetDstTag(pHpd, 0x0000); /* Not used */
    CSL_udmapCppi5SetReturnPolicy(
    pHpd,
    descType,
    CSL_UDMAP_CPPI5_PD_PKTINFO2_RETPOLICY_VAL_ENTIRE_PKT,
    CSL_UDMAP_CPPI5_PD_PKTINFO2_EARLYRET_VAL_NO,
    CSL_UDMAP_CPPI5_PD_PKTINFO2_RETPUSHPOLICY_VAL_TO_TAIL,
    cqRingNum);
    CSL_udmapCppi5LinkDesc(pHpd, 0U);
    CSL_udmapCppi5SetBufferAddr(pHpd, (uint64_t) Udma_defaultVirtToPhyFxn(buffer, 0U, NULL));
    CSL_udmapCppi5SetBufferLen(pHpd, length);
    CSL_udmapCppi5SetOrgBufferAddr(pHpd, (uint64_t) Udma_defaultVirtToPhyFxn(buffer, 0U, NULL));
    CSL_udmapCppi5SetOrgBufferLen(pHpd, length);

    /* Writeback cache */
    CacheP_wbInv(pUdmaHpdMem, sizeof(CSL_UdmapCppi5HMPD), CacheP_TYPE_ALLD);

    }

  • Hi Robert,

    I went through your code quickly. I did found anything wrong. The only suggestion I have is trying to enable a UDMA instance through the SYSConfig. The example you can refer to is ospi_flash_dma_am64x-evm_r5fss0-0_nortos_ti-arm-clang.

    Best regards,

    Ming

  • Hi Ming,

    I have set this thread to resolved by mistake, can you reopen it? I will check the example.

    Best regards,

    Robert

  • Hi Robert,

    I did re-opened it by sending the post three days ago after your marking "Resolved". Please let me know how it goes.

    Best regards,

    Ming

  • Hi Ming,

    I had a look on the ospi flash dma example. Here the BCDMA (block copy DMA) is used . The flash is mapped into memory addresses. And the dma is triggered from software. I could not recognize how i can trigger a BCDMA channel, with the DMA event of the MCSPI.

    If I want to trigger the DMA by the periphery, do I have to use the PKDMA (package DMA) or can I also trigger the BCDMA with the MCSPI dma event? 

    Best regards,

    Robert

  • Hi Robert,

    I am sorry for pointing to the wrong example. OSPI DMA example is using BDMA.

    You are correct. The McSPI0-3 are using the PDMA0 and the McSPI4 is using PDMA1. The example you should have referred is udma_adc_read_am64x-evm_r5fss0-0_freertos_ti-arm-clang which uses the PDMA0. For McSPI DMA event, refer to Table 12-467. MCSPI Hardware Requests in TRM. For  McSPI DMA delatis refer to 12.1.4.4.8 MCSPI DMA Requests in TRM. 

    Best reagrds,

    Ming

  • Hi Ming,

    the ADC example is similar to my code above. I have checked for differences, but it still does not work. Also i've already configured the DMA request with setting DMAW in MCSPI_CHCONF_0. 
    But during test I could see, that the UDMA_EVENT_TYPE_DMA_COMPLETION happen  directly after calling Udma_ringQueueRaw. Requesting the stats with Udma_chGetStats, shows the number of packages and bytes which should be transfered, but no data was really transfered to the MCSPI periphery. Also the element count is set to 32Byte, so DMA must not be finished until the SPI Slave was accessed at least one time for the first 32 Bytes.

    - Is there an additional configuration necessary to connect the MCSPI with CBASS0 or shall the DMA could access the MCSPI periphery by default.

    - Is the required peerChNum really 0xC304 for SPI1_CH0_TX?

    - In the manual at chapter 11.3.2.1 the channel is enumerated with 8004 (expect 0x8004). In the header file the channel is numerated with 0xC304. Without the added 0x4300 it fails at initialization  time. Can you tell me what adding 0x4300 does to the channel number?

    -The chapter 11.3.2.1 provides the RX channel numbers in decimal system, where the TX channels are provided in hexadezimal system. This might need to be corrected. (SPRUIM2C – MAY 2020 – REVISED SEPTEMBER 2021)

    - Is there a SPI driver with DMA planed in future? And if so when can I expect to work with it?

    Best regards,

    Robert

  • Hi Robert,

    -- I do not know the answers to first question. I have asked our software development team for answer Will get back to you soon.

    -- I do know the answer to the "peerChNum really is 0xC304 for SPI1_CH0_TX". I think the answer is yes. The reason I said  yes is because the example crypto_sha_hw_am64x-evm_r5fss0-0_nortos_ti-arm-clang actually does something similar for SA2UL

    In udma_soc.h,

    #define UDMA_PSIL_DEST_THREAD_OFFSET    (0x8000U)

    #define UDMA_PSIL_CH_SAUL0_RX (0x4000U)

    #define UDMA_PSIL_CH_SAUL0_TX           (UDMA_PSIL_CH_SAUL0_RX | UDMA_PSIL_DEST_THREAD_OFFSET)

    With

    #define UDMA_PSIL_CH_ICSS_G0_RX (0x4100U)
    #define UDMA_PSIL_CH_ICSS_G1_RX (0x4200U)

    We can infer that 0x4300 is for MCSPI, it was confirmed in 

    #define UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_RX    (0x4300U + 4U)

    #define UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_TX    (UDMA_PDMA_CH_MAIN0_MCSPI1_CH0_RX | UDMA_PSIL_DEST_THREAD_OFFSET).

    You can refer to the crypto_sha_hw_am64x-evm_r5fss0-0_nortos_ti-arm-clang for details, because this example also uses the PDMA0 for TX transfer. I noticed this example uses the UDMA_CH_TYPE_TX_MAPPED instead of the UDMA_CH_TYPE_PDMA_TX for channel type. Maybe you can try it.

    -- I did see the inconsistency of the PDMA channel # for RX and TX. I file a JIRA ticket for this for document update.

    -- There is no UDMA support for MCSPI at this point. I also asked the software development team for the future schedule for the UDMA support for MCSPI.

    Best regards,

    Ming

  • Hi Ming, 
    now I have a running version with MCSPI1 as Master using DMA. But operation as SLAVE still does not work. The TX DMA completes imediately like in operation as Master. But the DMA TX Transfer will not be executed when enabling the MSCPI DMA Flags like in Master mode. In my opinion the DMA will not be triggered in MCSPI Slave mode. 

    Can you check this with the development team? 

    I wrote the code by rewriting the ADC DMA example for the AM64x_EVM. If you want I can provide you this code (.zip, ~300kb). Just tell me where to mail or to upload.

    If my suggestion becomes true, we must change our system system design to make the other part beeing the Slave and the Sitara the Master.

  • Hi Robert,

    I talked to the software development team. They have confirmed the schedule for MCSPI DMA support is md of Dec. This schedule has been communicated to your company last week. Would you mind close this thread. If the MCU+ SDK update in mid Dec. still not working, you can start a new thread.

    Best regards,

    Ming

  • Hi Robert,

    You can send your code to mwei@ti.com. Please remember the linker.cmd will be blocked by the email server, so you have to rename it to linker.lcf. Thanks!

    Best regards,

    Ming