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.

TMS320F28075: Enabling interrupt-based data transfer from SDFM to SPI with DMA

Part Number: TMS320F28075


Hello everybody,

I'm trying to setup a DMA from the SDFM to a high-speed SPI interface in order to transfer all captured data samples over a connected FTDI cable with MPSSE to a PC. In my opinion the DMA is probably not well configured but I'm not familiar in debugging this mechanism. The SDFM is already working in the system, and the SPI and DMA are newly implemented. On the uC I have connected the SPI signals to a scope in order to visualize if some data is coming out of the processor. The configuration sequence is as follows:

1. Configure corresponding GPIOs of SDFM and SPI, e.g. SPI SIMO signal.

    GPIO_setDirectionMode (SPI_SIMO_PIN, GPIO_DIR_MODE_OUT);
    GPIO_setMasterCore(SPI_SIMO_PIN, GPIO_CORE_CPU1);
    GPIO_setPadConfig (SPI_SIMO_PIN, GPIO_PIN_TYPE_PULLUP);
    GPIO_setQualificationMode(SPI_SIMO_PIN, GPIO_QUAL_ASYNC);
    GPIO_setPinConfig (GPIO_58_SPISIMOA);

2. Initialize SDFM module.

  // Reset SDFM1 module
  SysCtl_resetPeripheral (SYSCTL_PERIPH_RES_SD1);

  // Configure and enable interrupts
  Interrupt_clearACKGroup (INTERRUPT_ACK_GROUP5);
  Interrupt_enable (INT_SD1);

  // Input Control Unit
  SDFM_setupModulatorClock (SDFM1_BASE, SDFM_FILTER_1,
                            SDFM_MODULATOR_CLK_EQUAL_DATA_RATE);

  SDFM_setupModulatorClock (SDFM1_BASE, SDFM_FILTER_2,
                            SDFM_MODULATOR_CLK_EQUAL_DATA_RATE);

  // Data Filter Unit
  SDFM_configDataFilter (SDFM1_BASE,
      (SDFM_FILTER_1 | SDFM_FILTER_SINC_3 | SDFM_SET_OSR(200)),
      (SDFM_DATA_FORMAT_16_BIT | SDFM_FILTER_ENABLE | SDFM_SHIFT_VALUE(0x0008)));

  SDFM_configDataFilter (SDFM1_BASE,
      (SDFM_FILTER_2 | SDFM_FILTER_SINC_3 | SDFM_SET_OSR(200)),
      (SDFM_DATA_FORMAT_16_BIT | SDFM_FILTER_ENABLE | SDFM_SHIFT_VALUE(0x0008)));

  // Enable Master filter bit
  SDFM_enableMasterFilter (SDFM1_BASE);

  // Disable external resets
  SDFM_disableExternalReset (SDFM1_BASE, SDFM_FILTER_1);
  SDFM_disableExternalReset (SDFM1_BASE, SDFM_FILTER_2);

  // Enable interrupts
  SDFM_enableInterrupt (SDFM1_BASE, SDFM_FILTER_1,
                        SDFM_DATA_FILTER_ACKNOWLEDGE_INTERRUPT);
  SDFM_enableInterrupt (SDFM1_BASE, SDFM_FILTER_2,
                        SDFM_DATA_FILTER_ACKNOWLEDGE_INTERRUPT);
  SDFM_disableInterrupt (SDFM1_BASE, SDFM_FILTER_1,
                         (SDFM_HIGH_LEVEL_THRESHOLD_INTERRUPT |
                         SDFM_LOW_LEVEL_THRESHOLD_INTERRUPT |
                         SDFM_MODULATOR_FAILURE_INTERRUPT));
  SDFM_disableInterrupt (SDFM1_BASE, SDFM_FILTER_2,
                         (SDFM_HIGH_LEVEL_THRESHOLD_INTERRUPT |
                         SDFM_LOW_LEVEL_THRESHOLD_INTERRUPT |
                         SDFM_MODULATOR_FAILURE_INTERRUPT));

  // Enable master interrupt so that any of the filter interrupts can trigger
  // by SDFM interrupt to CPU
  SDFM_enableMasterInterrupt (SDFM1_BASE);

3. Initialize interrupts for DMA module.

    // Configure and enable interrupts
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    Interrupt_register(INT_DMA_CH6, &dmaCh6ISR);
    Interrupt_enable(INT_DMA_CH6);

    // Initialize DMA controller
    DMA_initController();
    DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);

4. Initialize SPI module

    // Reset SPI module
    SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_SPIA);

    // Configure and enable interrupts
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
    Interrupt_register(INT_SPIA_TX, &spiTxISR);
    Interrupt_enable(INT_SPIA_TX);

    // Must disable SPI module before configuring it
    SPI_disableModule(SPIA_BASE);

    // Hardware configuration
    SPI_setConfig(SPIA_BASE, (uint32_t) DEVICE_LSPCLK_FREQ,
                  SPI_PROT_POL0PHA0, SPI_MODE_MASTER,
                  (uint32_t) SPI_BAUD_RATE, (uint16_t) SPI_WORD_WIDTH);
    SPI_enableHighSpeedMode(SPIA_BASE);
    SPI_setEmulationMode(SPIA_BASE, SPI_EMULATION_FREE_RUN);
    SPI_disableTriWire(SPIA_BASE);
    SPI_disableLoopback(SPIA_BASE);

    // FIFO and interrupt configuration
    SPI_enableFIFO(SPIA_BASE);
    SPI_resetTxFIFO(SPIA_BASE);
    SPI_resetRxFIFO(SPIA_BASE);
    SPI_setFIFOInterruptLevel(SPIA_BASE, SPI_TX_FIFO_INTLEV, SPI_FIFO_RXDEFAULT);
    SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_TXFF);
    SPI_enableInterrupt(SPIA_BASE, SPI_INT_TXFF);
    SPI_disableInterrupt(SPIA_BASE, SPI_INT_RXFF);

    // Enable SPI module
    SPI_enableModule(SPIA_BASE);

After these steps the uC enters the infinite loop where a command parser is running. Over a terminal program from the PC I can set the number of samples and the SDFM channel to be captured. After that a separate command is used to trigger the function hsdlStart(void) to parameterize the DMA channel, start the transfer and wait until finished. And here it stucks since the job never gets done! The code of the mentioned is as follows:

#define SPI_BAUD_RATE           (DEVICE_LSPCLK_FREQ / 4)
#define SPI_WORD_WIDTH          16
#define SPI_TX_FIFO_INTLEV      SPI_FIFO_TX4

void hsdlStart (void)
{
    uint16_t dmaBurstSize;
    uint32_t dmaTransferSize;
    const void *srcAddr;
    const void *destAddr;

    // Ensure that DMA is connected to bridge of Peripheral Frame 1 and 2
    EALLOW;
    HWREG(CPUSYS_BASE + SYS_SECMSEL) = 0x5;
    EDIS;

    // Calculate DMA burst and transfer size
    dmaBurstSize = 16 - SPI_TX_FIFO_INTLEV;
    dmaTransferSize = hsdlParams.nSamples / SPI_TX_FIFO_INTLEV;

    // Set DMA burst and transfer size
    DMA_configBurst(DMA_CH6_BASE,dmaBurstSize,1,1);
    DMA_configTransfer(DMA_CH6_BASE,dmaTransferSize,1,1);

    // Set pointer to destination address
    destAddr = (const void *)(SPIA_BASE + SPI_O_TXBUF);

    // Configure DMA channel 6
    if (hsdlParams.dataSel == 0)
    {
        // Set pointer to source address
        srcAddr = (const void *)(SDFM1_BASE + SDFM_O_SDDATA1 + 0x1U);       // +1 to set pointer to lower 16 bits

        // Configure DMA mode
        DMA_configMode(DMA_CH6_BASE,DMA_TRIGGER_SDFM1FLT1,
                       DMA_CFG_ONESHOT_ENABLE | DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);
    }
    else if (hsdlParams.dataSel == 1)
    {
        // Set pointer to source address
        srcAddr = (const void *)(SDFM1_BASE + SDFM_O_SDDATA2 + 0x1U);       // +1 to set pointer to lower 16 bits

        // Configure DMA mode
        DMA_configMode(DMA_CH6_BASE,DMA_TRIGGER_SDFM1FLT2,
                       DMA_CFG_ONESHOT_ENABLE | DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);
    }
    DMA_configAddresses(DMA_CH6_BASE,destAddr,srcAddr);
    DMA_setInterruptMode(DMA_CH6_BASE,DMA_INT_AT_END);
    DMA_enableTrigger(DMA_CH6_BASE);
    DMA_enableInterrupt(DMA_CH6_BASE);

    // Start DMA channel 6
    DMA_startChannel(DMA_CH6_BASE);
    DMA_forceTrigger(DMA_CH6_BASE);

    // Show debug output and LED that HSDL transfer has started
    printf ("*** HSDL transfer started ***\n");
    GPIO_writePin (GPIO_PIN_STATUS_LED_3, 0);

    // Enable flag to indicate that HSDL transfer is started
    // --> disabled in "__interrupt void dmaCh6ISR(void)"
    hsdlParams.running = 1;

    // Wait until HSDL transfer is finished
    while (hsdlParams.running == 1) { }

    // Show debug output and LED that HSDL transfer has completed
    printf ("*** HSDL transfer completed ***\n");
    GPIO_writePin (GPIO_PIN_STATUS_LED_3, 1);

    // Ensure that CLA is connected to bridge of Peripheral Frame 1 and 2
    EALLOW;
    HWREG(CPUSYS_BASE + SYS_SECMSEL) = 0x0;
    EDIS;
}

__interrupt void dmaCh6ISR(void)
{
    // Stop DMA channel 6
    DMA_stopChannel(DMA_CH6_BASE);

    // Disable flag to indicate that HSDL transfer is completed
    hsdlParams.running = 0;

    // Acknowledge this interrupt to receive more interrupts from group 7
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

__interrupt void spiTxISR(void)
{
    // Clear interrupt flag
    SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_TXFF);

    // Acknowledge this interrupt to receive more interrupts from group 6
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
}

When debugging the code it never gets out of the while loop in hsdlStart(), so I guess it has something to do with not properly configured interrupts because the ISRs are never entered. It would be great if anyone has an idea what's going wrong in my setup.

Best regards

Steffen

  • Steffen,

    If the DMA ISR is not executing, I would suspect that either the DMA is not being triggered or the PIE is not configured to service the DMA interrupt.

    You can confirm the ISR setup by manually forcing the PIEIFR7.6 bit to see if the ISR executes.  You can then confirm the PIE settings by manually forcing the DMA_CH6 trigger using the CONTROL[PERINTFRC] bit.  If both of these work, the problem would probably be with the SDFM trigger generation.

    -Tommy

  • Hello Tommy,

    thanks a lot for your helpful hints. I tried this out and it really seems that trigger generation is the problem. I need to dig deeper in that and let you know if I get it up and running.

    Beside the debugging stuff: Do you see any wrong parameterization of the DMA and SPI interface?

    Best regards

    Steffen
  • Steffen,

    I will notify the SPI and DMA experts to take a look.

    -Tommy
  • Hi Steffen,

    I do see an issue with your DMA configuration. Since your destination address is the TXBUF register, and you fill up the FIFO by writing to that register repeatedly, you should be passing 0 into the destStep of DMA_configBurst() and DMA_configTransfer(). I'm not very familiar with the SDFM module, but make sure you don't have a similar problem there.

    What is the highest possible value of nSamples? Since you have oneshot mode enabled in the DMA, I just want to make sure that there isn't a possibility that you'll be overflowing the FIFO by doing a whole transfer on a single trigger.

    Were you able to confirm that your interrupt configuration was good by forcing interrupts as Tommy suggested? Also, you mentioned that you had SPI signals hooked to an oscilloscope--were you able to see any activity?

    Whitney
  • Hi Steffen,

    Any progress debugging this issue?

    Whitney
  • Hi Whitney,

    thanks for your comments. The value of nSamples is configurable via command interface from the user. It is typically multiples of 4096, so for practical measurements let's say 10 * 4096.

    And yes, there still seems to be an interrupt problem as mentioned by Tommy. But right now I have no more progress on that, I'm sorry. As long as there's no activity on the SPI clock signal it does not work I guess. ;-) And that's the case right now. But do you see a general problem of the setup? I mean, it should be possible to shift data directly from SDFM to SPI output register via DMA, right?

    I further have to analyze/debug and then come back to you.

    Best regards

    Steffen
  • The high level idea of transferring data from SPI to SDFM seems okay. You have to take care that you aren't triggering transfers from the SDFM faster than the SPI can make room in its FIFO though.

    A good starting point might be to get SDFM and SPI working without the DMA--move the SDFM data to the SPI TX buffer by software in the SDFM ISR or something. Once you have that working, you'll know that your SDFM, SPI, and interrupt configuration is okay, and then you can worry about getting the DMA setup right.

    Whitney
  • Hi Whitney,

    this was a very good idea. I tried that and it works now. Many thanks! In the meantime I figured out that I probably do not really need a DMA for my application. So for the moment I take it as is and consider my issue as resolved.

    Best regards

    Steffen