Page 1 of 2

wspiReceive called twice, no cleared NDTR register  Topic is solved

Posted: Sun Feb 28, 2021 7:04 pm
by error414
Hi Giovanni,

I play with WSPI and I noticed weird issue. If I call twice wspiReceive(), then NDRT register for DMA is not cleared.

Simple code for test:

Code: Select all


#include "ch.h"
#include "hal.h"

int main(void) {
   halInit();
   chSysInit();
   wspiStart(&WSPID1, &config);

   /* NCSS */
   palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(10) | PAL_STM32_OSPEED_HIGHEST);

   /* SCK */
   palSetPadMode(GPIOF, 10, PAL_MODE_ALTERNATE(9) | PAL_STM32_OSPEED_HIGHEST);

   /* IO0 */
   palSetPadMode(GPIOF, 8, PAL_MODE_ALTERNATE(10) | PAL_STM32_OSPEED_HIGHEST);

   /* IO1 */
   palSetPadMode(GPIOF, 9, PAL_MODE_ALTERNATE(10) | PAL_STM32_OSPEED_HIGHEST);
   
   wspi_command_t mode;

   static uint8_t buffer1[512];
   static uint8_t buffer2[512];
   static uint8_t buffer3[512];

   mode.cmd   = 0x9f;
   mode.cfg   = (WSPI_CFG_CMD_MODE_ONE_LINE       | \
                 WSPI_CFG_ADDR_MODE_NONE          | \
                 WSPI_CFG_ALT_MODE_NONE           | \
                 WSPI_CFG_DATA_MODE_ONE_LINE       | \
                 WSPI_CFG_CMD_SIZE_8              | \
                 WSPI_CFG_ADDR_SIZE_24);
   mode.addr  = 0U;
   mode.alt   = 0U;
   mode.dummy = 0U;

   wspiReceive(&WSPID1, &mode, 3, buffer1);
   
   
   //ends in infinity while wspi_lld_serve_interrupt   "while (dmaStreamGetTransactionSize(wspip->dma) > 0U)"
   wspiReceive(&WSPID1, &mode, 3, buffer2);
   
   
   
   wspiReceive(&WSPID1, &mode, 3, buffer3);

while (true) {
   chThdSleepMilliseconds(1000);
}   


Second calling of " wspiReceive(&WSPID1, &mode, 3, buffer2);" ends in infinity "while" because NDTR is not cleared, I checked communication in QSPI pin and all what should be send, were send.

Infinity while:

Code: Select all

static void wspi_lld_serve_interrupt(WSPIDriver *wspip) {

  /* Portable WSPI ISR code defined in the high level driver, note, it is
     a macro.*/
  _wspi_isr_code(wspip);

  /* Stop everything, we need to give DMA enough time to complete the ongoing
     operation. Race condition hidden here.*/
  while (dmaStreamGetTransactionSize(wspip->dma) > 0U)  <-------------------------
    ;

  /* Handling of errata: Extra data written in the FIFO at the end of a
     read transfer.*/
  if (wspip->state == WSPI_RECEIVE) {
    while ((wspip->qspi->SR & QUADSPI_SR_BUSY) != 0U) {
      (void) wspip->qspi->DR;
    }
  }
}




Does not metter whether memory is connected or not, in both case same behavior. If memory is connected then firts calling "wspiReceive" fills buffer1 with propper data, so communication with memory works, at least for first "wspiReceive" calling.

My mcuconf.h
https://pastebin.com/GXneeyy7
/*
* WSPI driver system settings.
*/
#define STM32_WSPI_USE_QUADSPI1 TRUE
#define STM32_WSPI_QUADSPI1_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) // stream (2, 7) = the same issue
#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 255


* Latest CHibiOs from github
* STM32F767Zi nucleo 144

Re: wspiReceive called twice, no cleared NDTR register

Posted: Sun Feb 28, 2021 7:24 pm
by Giovanni
Hi,

Moving in bug reports, I will give it a try probably tomorrow.

Giovanni

Re: wspiReceive called twice, no cleared NDTR register

Posted: Tue Mar 02, 2021 12:16 pm
by error414
Small progress.

It started to work when I added "dmaStreamClearInterrupt(wspip->dma);". I'm not sure whether it's right solution, but it works

Code: Select all

--- a/ChibiOS/os/hal/ports/STM32/LLD/QUADSPIv1/hal_wspi_lld.c   (revision 2bf5987875f11e02acc4f20eda412fabe7f6dafc)
+++ b/ChibiOS/os/hal/ports/STM32/LLD/QUADSPIv1/hal_wspi_lld.c   (date 1614683492813)
@@ -299,6 +299,7 @@
 void wspi_lld_receive(WSPIDriver *wspip, const wspi_command_t *cmdp,
                       size_t n, uint8_t *rxbuf) {
 
+  dmaStreamClearInterrupt(wspip->dma);
   dmaStreamSetMemory0(wspip->dma, rxbuf);
   dmaStreamSetTransactionSize(wspip->dma, n);
   dmaStreamSetMode(wspip->dma, wspip->dmamode | STM32_DMA_CR_DIR_P2M);


Re: wspiReceive called twice, no cleared NDTR register

Posted: Tue Mar 02, 2021 1:10 pm
by Giovanni
Thanks, to be verified if it is an effect of the problem or the cause.

Giovanni

Re: wspiReceive called twice, no cleared NDTR register

Posted: Tue Mar 02, 2021 11:00 pm
by FXCoder
Hi Giovanni,
I was doing some work with QSPI last year which had to be shelved due to other priorities.
Getting back on to that project soon though.

I did make some minor changes to QUADSPIv1 which were not extensively tested and there is likely more to be done...
--
Bob

Code: Select all

Index: hal_wspi_lld.c
===================================================================
--- hal_wspi_lld.c   (revision 14056)
+++ hal_wspi_lld.c   (working copy)
@@ -68,7 +68,6 @@
  */
 static void wspi_lld_serve_dma_interrupt(WSPIDriver *wspip, uint32_t flags) {
 
-  (void)wspip;
   (void)flags;
 
   /* DMA errors handling.*/
@@ -77,6 +76,7 @@
     STM32_WSPI_DMA_ERROR_HOOK(wspip);
   }
 #endif
+  dmaStreamClearInterrupt(wspip->dma);
 }
 
 /**
@@ -86,10 +86,6 @@
  */
 static void wspi_lld_serve_interrupt(WSPIDriver *wspip) {
 
-  /* Portable WSPI ISR code defined in the high level driver, note, it is
-     a macro.*/
-  _wspi_isr_code(wspip);
-
   /* Stop everything, we need to give DMA enough time to complete the ongoing
      operation. Race condition hidden here.*/
   while (dmaStreamGetTransactionSize(wspip->dma) > 0U)
@@ -102,6 +98,10 @@
       (void) wspip->qspi->DR;
     }
   }
+
+  /* Portable WSPI ISR code defined in the high level driver, note, it is
+     a macro.*/
+  _wspi_isr_code(wspip);
 }
 
 /*===========================================================================*/


Re: wspiReceive called twice, no cleared NDTR register

Posted: Thu Mar 04, 2021 12:15 pm
by error414
Thank you very much for comment FXCoder. Unfortunately the patch does't work me because "wspi_lld_serve_dma_interrupt" is not never called. Flags for enable DMA interupt are set to 0x0; There are enabled only flags for DMA error interrupts.

I can post all values from registers if needed.

Re: wspiReceive called twice, no cleared NDTR register

Posted: Fri Mar 05, 2021 12:47 am
by FXCoder
Hi,
Interesting that neither TCIE or HTIE are enabled yet there would seem to be a lingering ISR or other status blocking the next operation.
Inspecting the DMA ISR after the first receive could give a clue.
--
Bob

Re: wspiReceive called twice, no cleared NDTR register

Posted: Fri Mar 05, 2021 8:17 am
by Giovanni
FXCoder wrote:Hi,
Interesting that neither TCIE or HTIE are enabled yet there would seem to be a lingering ISR or other status blocking the next operation.
Inspecting the DMA ISR after the first receive could give a clue.


This is exactly what I want to do as soon I have some time for this, the 1st operation leaves the DMA and/or QSPI in an unfinished state probably. It is possible this is specific to the command format used, I have not seen problems using the existing flash infrastructure.

Giovanni

Re: wspiReceive called twice, no cleared NDTR register

Posted: Fri Mar 05, 2021 3:27 pm
by error414
I'm so sorry for screenshots, it's not possible to copy whole register list :(

Screenshots are captured after first call "wspiReceive(&WSPID1, &mode, 3, buffer1);"

(two screenshots for DMA2, one for QSPI)

BTW: I use DMA2 Channel2
#define STM32_WSPI_QUADSPI1_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)

Re: wspiReceive called twice, no cleared NDTR register

Posted: Sun Mar 07, 2021 9:10 am
by Giovanni
Hi,

I have been able to reproduce the problem. The root cause is that the DMA interrupt is not enabled so the DMA ISR is not invoked in the DMA driver, flags are cleared there.

This is the fix I committed on trunk, apparently it works, could you confirm? I will back-port this to stable branches after confirmation.

Code: Select all

static void wspi_lld_serve_interrupt(WSPIDriver *wspip) {

  /* Portable WSPI ISR code defined in the high level driver, note, it is
     a macro.*/
  _wspi_isr_code(wspip);

  /* Stop everything, we need to give DMA enough time to complete the ongoing
     operation. Race condition hidden here.*/
  while (dmaStreamGetTransactionSize(wspip->dma) > 0U)
    ;

  /* Clearing DMA interrupts here because the DMA ISR is not called on
     transfer complete.*/
  dmaStreamClearInterrupt(wspip->dma);

  /* Handling of errata: Extra data written in the FIFO at the end of a
     read transfer.*/
  if (wspip->state == WSPI_RECEIVE) {
    while ((wspip->qspi->SR & QUADSPI_SR_BUSY) != 0U) {
      (void) wspip->qspi->DR;
    }
  }
}


Traced as bug #1147.

Giovanni