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-AM65X: Issue in OSPI driver to access serial NOR flash

Part Number: PROCESSOR-SDK-AM65X
Other Parts Discussed in Thread: AM6548, SEGGER

Dear experts,

we have issues in realtime behaviour when polling status register of a serial NOR flash:
the interrupts are disabled for a long period (250µs) which impacts realtime behavior heavily.

Our environment is:

  • Sitara AM6548 with a custom board
  • AM65xx processor SDK v6.3  (in v8.02 seem to have the same issue)
  • TI-RTOS on A53, but I think with FreeRtos at R5 it would have the same issue
  • Quad SPI interface accessing an ISSI NOR flash IS25LP256E
  • An embedded filesystem accessing the NOR flash (Segger emFile)

The issue is that during polling NOR flash status register, the global interrupts are disabled and polling needs up to 250µs (!).
I located the root cause of the issue in PDK\packages\ti\drv\spi\src\v0\OSPI_v0.c in the function OSPI_transfer_v0()

        key = SPI_osalHardwareIntDisable();
		...
        else
        {
            /* Save the pointer to the transaction */
            object->transaction = transaction;

            /* Acquire the lock for this particular I2C handle */
            (void)SPI_osalPendLock(object->mutex, SemaphoreP_WAIT_FOREVER);

            /* Book keeping of transmit and receive buffers. */
            object->writeBufIdx = (uint8_t *)transaction->txBuf;
            object->writeCountIdx = (uint32_t)transaction->count;
            object->readBufIdx =  (uint8_t *)transaction->rxBuf;
            object->readCountIdx = (uint32_t)transaction->count;

             /*
             * OSPI_primeTransfer_v0 is a longer process and
             * protection is needed from the OSPI interrupt
             */
            if ((uint32_t)SPI_OPER_MODE_POLLING != object->intrPollMode)
            {
                SPI_osalHardwareIntrEnable((int32_t)hwAttrs->intrNum, (int32_t)hwAttrs->eventId);
            }

            xferRet = OSPI_primeTransfer_v0(handle, transaction);
            SPI_osalHardwareIntRestore(key);

This section has even 2 issues:

  1. during disabled interrupts, a call to SPI_osalPendLock() is done, which is generally not a good idea, since disabling interrupts blocks normal behavior of the scheduler. Just the task with the next lower priority would be executed.
  2. during disabled interrupts, a call to OSPI_primeTransfer_v0() is done, which takes in our case up to 250µs !

My fix is now to first re-enable interrupts, then call OSPI_primeTransfer_v0():

            // R.Wurth 2022-04-01: re-enable interrupt BEFORE the transfer,
            // because a transfer to poll ISSI flash status register takes sometimes up to 250us
            SPI_osalHardwareIntRestore(key);
            xferRet = OSPI_primeTransfer_v0(handle, transaction);
            //SPI_osalHardwareIntRestore(key);

(The call to SPI_osalPendLock() is not a issue for us since we have already a mutex at a higher level (file system API), therefore the call to SPI_osalPendLock() does never block)

I could post some oscilloscope pictures with debug-GPIOs were we can see the timing behavior, including what happens in OSPI_flashExecCmd()

Questions:

  • Why can OSPI_primeTransfer_v0() take so long for just polling a status register?
    In our case the Software-Triggered Instruction Generator (STIG) of the AM6548 is used, via OSPI_flashExecCmd()
  • Is there a real reason why TI disabled globally interrupts before calling OSPI_primeTransfer_v0() ?

Best regards,
Ruediger 

  • Hi Ruediger ,

    Apologies for the long delay in response.
    Is this issue still open. If yes, can you please mention the current state of the issue?

    Regards,
    Parth

  • Hi Parth,
    I solved this in our PDK driver file with the fix described in my post: "first re-enable interrupts, then call OSPI_primeTransfer_v0".
    This seem to work well for our product.

    But I wanted to confirm this by TI.
    I still don't understand how the STIG driven polling of the flash status register sometimes take so long.
    The STIG is not so well described in the TRM.

  • Which thing is taking longer than expected: Reading the status register of the flash once?  Or reading the status register until the busy bit goes to 0?  One read of the status register should take a predictable amount of time.  However, if the controller is polling the status register many times before the busy bit goes to 0, then the problem is outside the AM6548.  Could you post the scope pictures you mentioned?

  • Hello Zack

    the function OSPI_primeTransfer_v0() takes up to 250µs with interrupts disabled.
    In this case the call stack is:

    OSPI_primeTransfer_v0()
    OSPI_read_v0()
    OSPI_cmd_mode_read_v0()
    OSPI_cmdRead()
    OSPI_flashExecCmd()
    while () { CSL_ospiFlashExecCmdComplete() }

    In the source code in function OSPI_flashExecCmd() I put some comments:

        // Analysis R.Wurth 2022-04-01
        //   one loop: ~1us
        //   OSPI_delay(OSPI_CHECK_IDLE_DELAY): ~0.12us
        //   worse case total loop time seen with Oscilloscope: ~250us
        //GPIO_DEBUG6(1U);
        while (retry != 0U)
        {
            /* Check the command execution status */
            if (CSL_ospiFlashExecCmdComplete(pRegs) == TRUE)
            {
                break;
            }
            OSPI_delay(OSPI_CHECK_IDLE_DELAY);
            retry--;
        }
        //GPIO_DEBUG6(0U);
    

    I measured with GPIOs that the while() loop around CSL_ospiFlashExecCmdComplete() takes so long.
    My understanding is that the function CSL_ospiFlashExecCmdComplete() just checks the status of the STIG, not the content of the received data:

    In CSL_ospiFlashExecCmdComplete(): 

        /* Check the command execution in progess */
        status = CSL_REG32_FEXT(&pRegs->FLASH_CMD_CTRL_REG,
                                OSPI_FLASH_CFG_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_FLD);
    

    Am I wrong? Interpretation of the busy bit is done on a much higher level, completely outside of the PDK source code.

    Regards
      Ruediger

  • You are correct.   The part of the code that you found to take 250us is only the STIG execution, which should not be taking that long.  Can you try disabling and re-enabling the controller before the flashExecCommand() function?  You do that by waiting for the controller to be idle by checking the register field OSPI_CONFIG_REG[31], and then clearing register field OSPI_CONFIG_REG[0] o 0 and then setting it back to 1.  Try this immediately before entering flashExecCommand().

  • Hello Zack,

    thank you for your reply. 
    I can try this, but it will take a while since I am busy with other topics and then I am in holidays.
    I think I can come back on this in June.
    Maybe we can leave this thread open until then.

    Best regards,
    Ruediger

  • Ruediger,

    Have you had time to revisit this?

    -Zack

  • Zack,

    Not yet, sorry, after my holidays I stumbled into a lot of urgent topics.
    I try to find some time, but I cannot promise.
    If I would not be able to anser until end of this week, please close the topic, since our workaround works stable.

    Regards,
    Ruediger