Skip to content

Commit 7275ee8

Browse files
committed
[Nuvoton] Fix SPI DMA transfer
1. Disable unnecessary TX/RX threshold interrupts to avoid potential trap in DMA transfer 2. Start TX/RX DMA transfer simultaneously to fit H/W spec and avoid potential RX FIFO overflow issue
1 parent 9e72756 commit 7275ee8

File tree

4 files changed

+133
-12
lines changed

4 files changed

+133
-12
lines changed

targets/TARGET_NUVOTON/TARGET_M451/spi_api.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,45 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
408408
PDMA_INT_TRANS_DONE); // Interrupt type
409409
// Register DMA event handler
410410
dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL);
411-
412-
// Start tx/rx DMA transfer
411+
412+
/* Start tx/rx DMA transfer
413+
*
414+
* If we have both PDMA and SPI interrupts enabled and PDMA priority is lower than SPI priority,
415+
* we would trap in SPI interrupt handler endlessly with the sequence:
416+
*
417+
* 1. PDMA TX transfer done interrupt occurs and is well handled.
418+
* 2. SPI RX FIFO threshold interrupt occurs. Trap here because PDMA RX transfer done interrupt doesn't get handled.
419+
* 3. PDMA RX transfer done interrupt occurs but it cannot be handled due to above.
420+
*
421+
* To fix it, we don't enable SPI TX/RX threshold interrupts but keep SPI vector handler set to be called
422+
* in PDMA TX/RX transfer done interrupt handlers (spi_dma_handler_tx/spi_dma_handler_rx).
423+
*/
424+
#if 0
413425
spi_enable_vector_interrupt(obj, handler, 1);
414-
// NOTE: It is safer to start rx DMA first and then tx DMA. Otherwise, receive FIFO is subject to overflow by tx DMA.
426+
#else
427+
NVIC_SetVector(modinit->irq_n, handler);
428+
#endif
429+
/* Order to enable PDMA TX/RX functions
430+
*
431+
* H/W spec: In SPI Master mode with full duplex transfer, if both TX and RX PDMA functions are
432+
* enabled, RX PDMA function cannot be enabled prior to TX PDMA function. User can enable
433+
* TX PDMA function firstly or enable both functions simultaneously.
434+
* Per real test, it is safer to start RX PDMA first and then TX PDMA. Otherwise, receive FIFO is
435+
* subject to overflow by TX DMA.
436+
*
437+
* With the above conflicts, we enable PDMA TX/RX functions simultaneously.
438+
*/
439+
#if 0
415440
SPI_TRIGGER_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
416441
SPI_TRIGGER_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
442+
#else
443+
spi_base->PDMACTL |= (SPI_PDMACTL_TXPDMAEN_Msk | SPI_PDMACTL_RXPDMAEN_Msk);
444+
#endif
445+
446+
/* Don't enable SPI TX/RX threshold interrupts as commented above */
447+
#if 0
417448
spi_master_enable_interrupt(obj, 1);
449+
#endif
418450
}
419451
}
420452

targets/TARGET_NUVOTON/TARGET_M480/spi_api.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,44 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
413413
// Register DMA event handler
414414
dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL);
415415

416-
// Start tx/rx DMA transfer
416+
/* Start tx/rx DMA transfer
417+
*
418+
* If we have both PDMA and SPI interrupts enabled and PDMA priority is lower than SPI priority,
419+
* we would trap in SPI interrupt handler endlessly with the sequence:
420+
*
421+
* 1. PDMA TX transfer done interrupt occurs and is well handled.
422+
* 2. SPI RX FIFO threshold interrupt occurs. Trap here because PDMA RX transfer done interrupt doesn't get handled.
423+
* 3. PDMA RX transfer done interrupt occurs but it cannot be handled due to above.
424+
*
425+
* To fix it, we don't enable SPI TX/RX threshold interrupts but keep SPI vector handler set to be called
426+
* in PDMA TX/RX transfer done interrupt handlers (spi_dma_handler_tx/spi_dma_handler_rx).
427+
*/
428+
#if 0
417429
spi_enable_vector_interrupt(obj, handler, 1);
418-
// NOTE: It is safer to start rx DMA first and then tx DMA. Otherwise, receive FIFO is subject to overflow by tx DMA.
430+
#else
431+
NVIC_SetVector(modinit->irq_n, handler);
432+
#endif
433+
/* Order to enable PDMA TX/RX functions
434+
*
435+
* H/W spec: In SPI Master mode with full duplex transfer, if both TX and RX PDMA functions are
436+
* enabled, RX PDMA function cannot be enabled prior to TX PDMA function. User can enable
437+
* TX PDMA function firstly or enable both functions simultaneously.
438+
* Per real test, it is safer to start RX PDMA first and then TX PDMA. Otherwise, receive FIFO is
439+
* subject to overflow by TX DMA.
440+
*
441+
* With the above conflicts, we enable PDMA TX/RX functions simultaneously.
442+
*/
443+
#if 0
419444
SPI_TRIGGER_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
420445
SPI_TRIGGER_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
446+
#else
447+
spi_base->PDMACTL |= (SPI_PDMACTL_TXPDMAEN_Msk | SPI_PDMACTL_RXPDMAEN_Msk);
448+
#endif
449+
450+
/* Don't enable SPI TX/RX threshold interrupts as commented above */
451+
#if 0
421452
spi_master_enable_interrupt(obj, 1);
453+
#endif
422454
}
423455
}
424456

targets/TARGET_NUVOTON/TARGET_NANO100/spi_api.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,14 +450,39 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
450450
PDMA_IER_TD_IE_Msk); // Interrupt type
451451
// Register DMA event handler
452452
dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL);
453-
454-
// Start tx/rx DMA transfer
453+
454+
/* Start tx/rx DMA transfer
455+
*
456+
* If we have both PDMA and SPI interrupts enabled and PDMA priority is lower than SPI priority,
457+
* we would trap in SPI interrupt handler endlessly with the sequence:
458+
*
459+
* 1. PDMA TX transfer done interrupt occurs and is well handled.
460+
* 2. SPI RX FIFO threshold interrupt occurs. Trap here because PDMA RX transfer done interrupt doesn't get handled.
461+
* 3. PDMA RX transfer done interrupt occurs but it cannot be handled due to above.
462+
*
463+
* To fix it, we don't enable SPI TX/RX threshold interrupts but keep SPI vector handler set to be called
464+
* in PDMA TX/RX transfer done interrupt handlers (spi_dma_handler_tx/spi_dma_handler_rx).
465+
*/
455466
spi_enable_vector_interrupt(obj, handler, 1);
456-
// No TX/RX FIFO threshold interrupt
467+
/* No TX/RX FIFO threshold interrupt */
457468
spi_master_enable_interrupt(obj, 0, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK);
458-
// NOTE: It is safer to start rx DMA first and then tx DMA. Otherwise, receive FIFO is subject to overflow by tx DMA.
469+
470+
/* Order to enable PDMA TX/RX functions
471+
*
472+
* H/W spec: In SPI Master mode with full duplex transfer, if both TX and RX PDMA functions are
473+
* enabled, RX PDMA function cannot be enabled prior to TX PDMA function. User can enable
474+
* TX PDMA function firstly or enable both functions simultaneously.
475+
* Per real test, it is safer to start RX PDMA first and then TX PDMA. Otherwise, receive FIFO is
476+
* subject to overflow by TX DMA.
477+
*
478+
* With the above conflicts, we enable PDMA TX/RX functions simultaneously.
479+
*/
480+
#if 0
459481
SPI_TRIGGER_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
460482
SPI_TRIGGER_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
483+
#else
484+
spi_base->DMA |= (SPI_DMA_TX_DMA_EN_Msk | SPI_DMA_RX_DMA_EN_Msk);
485+
#endif
461486
PDMA_Trigger(obj->spi.dma_chn_id_rx);
462487
PDMA_Trigger(obj->spi.dma_chn_id_tx);
463488
}

targets/TARGET_NUVOTON/TARGET_NUC472/spi_api.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,13 +410,45 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
410410
0); // Interrupt type. No use here
411411
// Register DMA event handler
412412
dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL);
413-
414-
// Start tx/rx DMA transfer
413+
414+
/* Start tx/rx DMA transfer
415+
*
416+
* If we have both PDMA and SPI interrupts enabled and PDMA priority is lower than SPI priority,
417+
* we would trap in SPI interrupt handler endlessly with the sequence:
418+
*
419+
* 1. PDMA TX transfer done interrupt occurs and is well handled.
420+
* 2. SPI RX FIFO threshold interrupt occurs. Trap here because PDMA RX transfer done interrupt doesn't get handled.
421+
* 3. PDMA RX transfer done interrupt occurs but it cannot be handled due to above.
422+
*
423+
* To fix it, we don't enable SPI TX/RX threshold interrupts but keep SPI vector handler set to be called
424+
* in PDMA TX/RX transfer done interrupt handlers (spi_dma_handler_tx/spi_dma_handler_rx).
425+
*/
426+
#if 0
415427
spi_enable_vector_interrupt(obj, handler, 1);
416-
// NOTE: It is safer to start rx DMA first and then tx DMA. Otherwise, receive FIFO is subject to overflow by tx DMA.
428+
#else
429+
NVIC_SetVector(modinit->irq_n, handler);
430+
#endif
431+
/* Order to enable PDMA TX/RX functions
432+
*
433+
* H/W spec: In SPI Master mode with full duplex transfer, if both TX and RX PDMA functions are
434+
* enabled, RX PDMA function cannot be enabled prior to TX PDMA function. User can enable
435+
* TX PDMA function firstly or enable both functions simultaneously.
436+
* Per real test, it is safer to start RX PDMA first and then TX PDMA. Otherwise, receive FIFO is
437+
* subject to overflow by TX DMA.
438+
*
439+
* With the above conflicts, we enable PDMA TX/RX functions simultaneously.
440+
*/
441+
#if 0
417442
SPI_TRIGGER_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
418443
SPI_TRIGGER_TX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
444+
#else
445+
spi_base->PDMACTL |= (SPI_PDMACTL_TXPDMAEN_Msk | SPI_PDMACTL_RXPDMAEN_Msk);
446+
#endif
447+
448+
/* Don't enable SPI TX/RX threshold interrupts as commented above */
449+
#if 0
419450
spi_master_enable_interrupt(obj, 1);
451+
#endif
420452
}
421453
}
422454

0 commit comments

Comments
 (0)