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.

TMS570LC4357: Synchronizing DMA request to SPI transmit while not using MibSPI

Part Number: TMS570LC4357

I am trying to DMA a block of 2048 bytes to the SPI port (not MibSPI) but it only sends part of the data. at 25MHz it shouid take over 600uS but I get notification after 150uS.  If I change to 18.8Mhz SPI clock, the transfer time does not change If I send a shorter block, the transfer time is shorter. After doing some testing i realize that the transmit time I see is equal to the DMA SRAM read time, not the SPI transmit. I am new to using DMA on ARM, but I have to admit that I was quite surprised that these two did not automatically synchronize.  How do I synchronize the SPI transmit to the DMA?

  • Hello Darryl,

    How many bytes of data are transferred? Can you share your DMA settings with us? 

    Is 150us for transferring 2048 bytes of data? What DMA interrupt is used in your code: HBC, BTC? 

    The DMA request enable bit (DMA REQ EN) controls the assertion of requests to the DMA controller module. When a character is being transmitted, the SPI will signal the DMA via the DMA request signals: TX_DMA_REQ. The DMA controller will then perform the required data transfer.

    The SPI generates a request on the TX_DMA_REQ line each time the TX data is copied to the TX shift register either from the TXBUF or from data register (SPIDAT0 or SPIDAT1, when TXBUF is empty).

    The DMA transfer and SPI transfer is synchronized by TX_DMA_REQ. Otherwise the SPIDATx will be overwritten.

  • 150uS is the time for 2048 bytes.  Sunil had sent a project that measured the time to DMA SRAM-to-SRAM and when I rebuilt it I measured 154uS for 2048 bytes.  I modified that project to do SRAM-to-SPI and I get a similar number, but  of course the SPI does not keep up.  Sunil had made a number of suggestions on configuration changes but all changes I tried resulted in either 16 bits transferred or zero bytes transferred. Below is the only configuration that actually transfers data albeit only a portion.  Beware that I have had some email contact with Sunil, so don't duplicate efforts :)

    #define E_COUNT 1 // Element count
    #define F_COUNT 2048 // Frame count
    #define D_SIZE E_COUNT * F_COUNT // Total size of data transfer

    void dmaConfigCtrlPacket(uint32 sadd, uint32 dadd, uint16 ElmntCnt, uint16 FrameCnt)
    {
    g_dmaCTRLPKT_RAM2SPI.SADD = sadd; // source address
    g_dmaCTRLPKT_RAM2SPI.DADD = dadd; // destination address
    g_dmaCTRLPKT_RAM2SPI.CHCTRL = 0; // no channel chaining
    g_dmaCTRLPKT_RAM2SPI.FRCNT = FrameCnt; // number of frames per block
    g_dmaCTRLPKT_RAM2SPI.ELCNT = ElmntCnt; // number of elements per frame
    g_dmaCTRLPKT_RAM2SPI.ELDOFFSET = 0; // element destination offset
    g_dmaCTRLPKT_RAM2SPI.ELSOFFSET = 0; // element destination offset
    g_dmaCTRLPKT_RAM2SPI.FRDOFFSET = 0; // frame destination offset
    g_dmaCTRLPKT_RAM2SPI.FRSOFFSET = 0; // frame destination offset
    g_dmaCTRLPKT_RAM2SPI.PORTASGN = PORTA_READ_PORTB_WRITE; // port A is shared SRAM, portB is peripheral
    g_dmaCTRLPKT_RAM2SPI.RDSIZE = ACCESS_32_BIT; // read size
    g_dmaCTRLPKT_RAM2SPI.WRSIZE = ACCESS_32_BIT; // write size
    g_dmaCTRLPKT_RAM2SPI.TTYPE = BLOCK_TRANSFER; // block transfer per trigger
    g_dmaCTRLPKT_RAM2SPI.ADDMODERD = ADDR_INC1; // address mode read
    g_dmaCTRLPKT_RAM2SPI.ADDMODEWR = ADDR_FIXED; // address mode write
    g_dmaCTRLPKT_RAM2SPI.AUTOINIT = AUTOINIT_OFF; // no auto init

    // Set control packet with initial source and destination addresses, #elements, #frames, and transfer sizes
    dmaConfigCtrlPacket((uint32)TXDATA, (&(spiREG1->DAT1)), E_COUNT, F_COUNT);

    // map DMA channel 0 to configured control packet
    dmaSetCtrlPacket(DMA_CH0, g_dmaCTRLPKT_RAM2SPI);

    // enable block transfer complete interrupt for DMA channel 0
    dmaEnableInterrupt(DMA_CH0, BTC, DMA_INTA);

    // start the DMA transfer

    dmaSetChEnable(DMA_CH0, DMA_SW);

    Changing the above line to DMA_HW results in no data transferred.  Since I am not reading the receive buffer, is it possible this inhibits DMA_REQ?

    Darryl
    }

  • Hi Darryl,

    Nice to know you got direct support from Sunil. 

    If the DMAREQEN bit in SPIINT0 register is not set, the DMA transfer doesn't start. Reading the receive buffer or not doesn't affect DMA TX transfer. 

  • Thanks, good to know the receive buffer overrun is not the problem

    Setting the DMAREQEN did not have any effect. No data is sent.

    // start the DMA transfer
    dmaSetChEnable(DMA_CH0, DMA_HW);
    spiREG1->INT0 |= 1U << 16; // Enable DMA Request

    Darryl

  • Hello Darryl,

    Please add this lines after "spiREG1->INT0 |= 1U << 16;" to try

    spiREG1->GCR1 = (spiREG1->GCR1 & 0xFFFFFFFFU) | (0x1 << 24); //Enable SPI

    I guess this maybe done in your spiInit(), but in case.  

    The first TX_DMA_REQ pulse is generated when either of the following is true:
    1. DMAREQEN (SPIINT0[16]) is set to 1 while SPIEN (SPIGCR1[24]) is already 1.
    2. SPIEN (SPIGCR1[24]) is set to 1 while DMAREQEN (SPIINT0[16]) is already 1.

     

  • QJ,

     I have tried 

    dmaSetChEnable(DMA_CH0, DMA_HW);
    spiREG1->GCR1 |= (1U << 24); // Make sure SPI is enabled
    spiREG1->INT0 |= (1U << 16); // Enable DMA Request

    but still get zero bytes.  Also tried Clearing SpiEnable first in case edge was needed

    dmaSetChEnable(DMA_CH0, DMA_HW);
     spiREG1->INT0 &= ~(1U << 16); // DISABLE DMA Request
    spiREG1->GCR1 |= (1U << 24); // Make sure SPI is enabled
    spiREG1->INT0 |= (1U << 16); // Enable DMA Request

    Thus far I have not found a solution using DMA_HW that actually transmits any data.  Only DMA_SW sends data but not synchronized.to the SPI port

    dmaSetChEnable(DMA_CH0, DMA_HW);

    Darryl

  • Hello Darryl,

    Have you solved your problem? 

  • Qj,

        No I am still waiting for a working DMA-to-SPI example not using MibSPI which Sunil was working on. 

    Darryl

  • QJ,

    I have sent an example offline.

  • Few issues,

    1. Block transfer per trigger will try to transfer the whole block per trigger as the comment says, it should be set to FRAME_TRANSFER, explains why TX is overwritten and no sync!

    g_dmaCTRLPKT_RAM2SPI.TTYPE = BLOCK FRAME_TRANSFER; // block one frame transfer per trigger

    2. Find the DMA REQ LINE that is connected to SPI and assign to the DMA_CH0,

    dmaReqAssign(DMA_CH0, SPI_DMAREQ_31);

    3. Use HW request

    dmaSetChEnable(DMA_CH0, DMA_HW);

    Start transfer as suggested by QJ or using first manual SPI_TX write.

    Good luck

  • Using Sunil's notes below, I was able to get the SPI3-to-SPI1 example to work. With this as a reference it should be straightforward to get SPI 1 to transmit similarly. 

    "The DMA setup required for the <SPI3> TX side to work (independent of the RX) is:

         /* - configuring DMA TX control packets   */

        dmaConfigCtrlTxPacket((unsigned int)&TX_DATA, SPI3_TX_ADDR, 1, BlockSize);

        dmaSetCtrlPacket(DMA_CH1, g_dmaCTRLPKT_TX);

        // map DMA request from SPI3 TX event to DMA channel 1

        dmaReqAssign(DMA_CH1, DMA_REQ15);

        /* - setting the DMA channel to trigger on h/w request */

        dmaSetChEnable(DMA_CH1, DMA_HW);    //SPI3 TX, hardware triggering

    This is not dependent on the RX generating a DMA request for the TX. DMA request # 15 is mapped to MibSPI3 DMA event 0 or SPI3 TX DMA event, as described in the datasheet"