QSPI with DMA problem on TC234

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
User16286
Level 4
Level 4
First like received
I'm trying to use the QSPI with DMA, and as usual, the IfxQspi and IfxDMA libraries are extremely buggy.
I have fixed all the bugs I have found so far, and this is the current situation:

o QSPI triggers DMA to fill the TXFIFO and drain the RXFIFO
o DMA reads the transmit data and writes the receive data to the correct addresses
o DMA generates an end-of-transfer service request when TCOUNT = 0.
o Verified by checking DMA_CHCSR001.ICH = 1.
o The interrupt router is ignoring the request and does not generate an interrupt
o The service request is reaching the IR, because I can see SRC_DMACH1.SRR = 1
o The interrupt is winning arbitration, because INT_LWSR0.PN = 0x3d, and DMA1 is using interrupt 61

I'm trying to understand why the interrupt handler isn't being called even though the interrupt is winning arbitration?
What registers should I check?

Toshi
0 Likes
12 Replies
User16286
Level 4
Level 4
First like received
It looks like CPU0 is failing to acknowledge the service request, because INT_LASR0.ID != INT_LWSR0.PN.
From what I understand, the only reason this should occur is because the ECC is wrong.
However, I don't think I have control over the generated ECC.

Toshi
0 Likes
NeMa_4793301
Level 6
Level 6
10 likes received 10 solutions authored 5 solutions authored
Are interrupts enabled on the CPU? Check ICR.IE - interrupts are disabled by default.
0 Likes
User16286
Level 4
Level 4
First like received
UC_wrangler wrote:
Are interrupts enabled on the CPU? Check ICR.IE - interrupts are disabled by default.


I just checked...ICR.IE is zero!
Don't know how it was reset, but now I have a lead!
Thanks for the help!

Toshi
0 Likes
User16286
Level 4
Level 4
First like received
I found iLLD QSPI DMA transfers for length = 1 at 400 khz were broken.
I fixed it.
I found iLLD QSPI DMA transfers for length = 2 at 400 khz were broken.
I fixed that too.
Now I find iLLD QSPI DMA transfers for length = 6 at 400 khz are broken.
This iLLD QSPI driver redefines the word "broken" and takes it to the next level.
0 Likes
cwunder
Employee
Employee
5 likes given 50 likes received 50 solutions authored
I don't know what mode you are using the QSPI in. Previously you stated you needed it to be variable based on the data received in the same frame. This requires you to use software to control the chip select.

If you simply need to have 8-bit data moves of variable lengths using two DMA channels. Here is a simple bare metal example.

You simply need to re-initialize the data pointers and the TREL to the size you want for another exchange.
void DMA_QSPI3_Init(uint16_t trel){
/* DMA Channel 005 used for QSPI3 Transmit */
DMA_SADR005.U = (uint32_t)&txData[0];
DMA_DADR005.U = (uint32_t)&QSPI3_DATAENTRY0.U;

Ifx_DMA_CH_CHCFGR chcfgr_5 = {
.B.TREL = trel, /*Transfer Reload Value */
.B.BLKM = dma_chcfgrxxx_blkm_OneTransferHasOneMove,
.B.RROAT = dma_chcfgrxxx_rroat_ResetAfterEachTransfer,
.B.CHMODE = dma_chcfgrxxx_chmode_SingleMode,
.B.CHDW = dma_chcfgrxxx_chdw_DataBitWidth_8,
.B.PATSEL = dma_chcfgrxxx_patsel_NoPatternCompareOperation,
.B.PRSEL = dma_chcfgrxxx_prsel_HardwareRequestSelected,
.B.DMAPRIO = dma_chcfgrxxx_dmaprio_LowPrioritySelected,
};
DMA_CHCFGR005.U = chcfgr_5.U;

Ifx_DMA_CH_ADICR adicr_5 = {
.B.SMF = dma_adicrxxx_smf_AddressOffsetIsCHDWx1,
.B.INCS = dma_adicrxxx_incs_AddressOffsetIsAdded,
.B.DMF = dma_adicrxxx_dmf_AddressOffsetIsCHDWx1,
.B.INCD = dma_adicrxxx_incd_AddressOffsetIsSubtracted,
.B.CBLS = dma_adicrxxx_cbls_SourceAddress31to0,
.B.CBLD = dma_adicrxxx_cbld_DestinationAddress31to0,
.B.SHCT = dma_adicrxxx_shct_MoveOperation,
.B.SCBE = dma_adicrxxx_scbe_SourceCircularBufferDisabled,
.B.DCBE = dma_adicrxxx_dcbe_DestinationCircularBufferEnabled,
.B.STAMP = dma_adicrxxx_stamp_NoAction,
.B.ETRL = dma_adicrxxx_etrl_NoInterruptOnLostEvent,
.B.WRPSE = dma_adicrxxx_wrpse_WrapSourceBufferInterruptTriggerDiabled,
.B.WRPDE = dma_adicrxxx_wrpde_WrapDestinationBufferInterruptTriggerDiabled,
.B.INTCT = dma_adicrxxx_intct_InterruptChangingTCOUNTandEqualsIRDV,
.B.IRDV = 0, /*Interrupt Raise Detect Value*/
};
DMA_ADICR005.U = adicr_5.U;
DMA_TSR005.B.ECH = 1;

/* DMA Channel 006: used for QSPI3 Receive */
DMA_SADR006.U = (uint32_t)&QSPI3_RXEXIT.U;
DMA_DADR006.U = (uint32_t)&rxData[0];

Ifx_DMA_CH_CHCFGR chcfgr_6 = {
.B.TREL = trel, /*Transfer Reload Value */
.B.BLKM = dma_chcfgrxxx_blkm_OneTransferHasOneMove,
.B.RROAT = dma_chcfgrxxx_rroat_ResetAfterEachTransfer,
.B.CHMODE = dma_chcfgrxxx_chmode_SingleMode,
.B.CHDW = dma_chcfgrxxx_chdw_DataBitWidth_8,
.B.PATSEL = dma_chcfgrxxx_patsel_NoPatternCompareOperation,
.B.PRSEL = dma_chcfgrxxx_prsel_HardwareRequestSelected,
.B.DMAPRIO = dma_chcfgrxxx_dmaprio_LowPrioritySelected,
};
DMA_CHCFGR006.U = chcfgr_6.U;

Ifx_DMA_CH_ADICR adicr_6 = {
.B.SMF = dma_adicrxxx_smf_AddressOffsetIsCHDWx1,
.B.INCS = dma_adicrxxx_incs_AddressOffsetIsSubtracted,
.B.DMF = dma_adicrxxx_dmf_AddressOffsetIsCHDWx1,
.B.INCD = dma_adicrxxx_incd_AddressOffsetIsAdded,
.B.CBLS = dma_adicrxxx_cbls_SourceAddress31to0,
.B.CBLD = dma_adicrxxx_cbld_DestinationAddress31to0,
.B.SHCT = dma_adicrxxx_shct_MoveOperation,
.B.SCBE = dma_adicrxxx_scbe_SourceCircularBufferEnabled,
.B.DCBE = dma_adicrxxx_dcbe_DestinationCircularBufferDisabled,
.B.STAMP = dma_adicrxxx_stamp_NoAction,
.B.ETRL = dma_adicrxxx_etrl_NoInterruptOnLostEvent,
.B.WRPSE = dma_adicrxxx_wrpse_WrapSourceBufferInterruptTriggerDiabled,
.B.WRPDE = dma_adicrxxx_wrpde_WrapDestinationBufferInterruptTriggerDiabled,
.B.INTCT =dma_adicrxxx_intct_InterruptChangingTCOUNTandEqualsIRDV,
.B.IRDV = 0, /*Interrupt Raise Detect Value*/
};
DMA_ADICR006.U = adicr_6.U;
DMA_TSR006.B.ECH = 1;

/* Setup interrupt from DMA Channel 5 and 6 when the Move Transaction has finished */
SRC_DMACH5.U = (CPU0_SERVICE << TOS) | (1 << SRE) | INTPRIO_DMA_CH5;
SRC_DMACH6.U = (CPU0_SERVICE << TOS) | (1 << SRE) | INTPRIO_DMA_CH6;
}
0 Likes
User16286
Level 4
Level 4
First like received
cwunder wrote:
I don't know what mode you are using the QSPI in. Previously you stated you needed it to be variable based on the data received in the same frame. This requires you to use software to control the chip select.

If you simply need to have 8-bit data moves of variable lengths using two DMA channels. Here is a simple bare metal example.

You simply need to re-initialize the data pointers and the TREL to the size you want for another exchange.
void DMA_QSPI3_Init(uint16_t trel){
/* DMA Channel 005 used for QSPI3 Transmit */
DMA_SADR005.U = (uint32_t)&txData[0];
DMA_DADR005.U = (uint32_t)&QSPI3_DATAENTRY0.U;

Ifx_DMA_CH_CHCFGR chcfgr_5 = {
.B.TREL = trel, /*Transfer Reload Value */
.B.BLKM = dma_chcfgrxxx_blkm_OneTransferHasOneMove,
.B.RROAT = dma_chcfgrxxx_rroat_ResetAfterEachTransfer,
.B.CHMODE = dma_chcfgrxxx_chmode_SingleMode,
.B.CHDW = dma_chcfgrxxx_chdw_DataBitWidth_8,
.B.PATSEL = dma_chcfgrxxx_patsel_NoPatternCompareOperation,
.B.PRSEL = dma_chcfgrxxx_prsel_HardwareRequestSelected,
.B.DMAPRIO = dma_chcfgrxxx_dmaprio_LowPrioritySelected,
};
DMA_CHCFGR005.U = chcfgr_5.U;

Ifx_DMA_CH_ADICR adicr_5 = {
.B.SMF = dma_adicrxxx_smf_AddressOffsetIsCHDWx1,
.B.INCS = dma_adicrxxx_incs_AddressOffsetIsAdded,
.B.DMF = dma_adicrxxx_dmf_AddressOffsetIsCHDWx1,
.B.INCD = dma_adicrxxx_incd_AddressOffsetIsSubtracted,
.B.CBLS = dma_adicrxxx_cbls_SourceAddress31to0,
.B.CBLD = dma_adicrxxx_cbld_DestinationAddress31to0,
.B.SHCT = dma_adicrxxx_shct_MoveOperation,
.B.SCBE = dma_adicrxxx_scbe_SourceCircularBufferDisabled,
.B.DCBE = dma_adicrxxx_dcbe_DestinationCircularBufferEnabled,
.B.STAMP = dma_adicrxxx_stamp_NoAction,
.B.ETRL = dma_adicrxxx_etrl_NoInterruptOnLostEvent,
.B.WRPSE = dma_adicrxxx_wrpse_WrapSourceBufferInterruptTriggerDiabled,
.B.WRPDE = dma_adicrxxx_wrpde_WrapDestinationBufferInterruptTriggerDiabled,
.B.INTCT = dma_adicrxxx_intct_InterruptChangingTCOUNTandEqualsIRDV,
.B.IRDV = 0, /*Interrupt Raise Detect Value*/
};
DMA_ADICR005.U = adicr_5.U;
DMA_TSR005.B.ECH = 1;

/* DMA Channel 006: used for QSPI3 Receive */
DMA_SADR006.U = (uint32_t)&QSPI3_RXEXIT.U;
DMA_DADR006.U = (uint32_t)&rxData[0];

Ifx_DMA_CH_CHCFGR chcfgr_6 = {
.B.TREL = trel, /*Transfer Reload Value */
.B.BLKM = dma_chcfgrxxx_blkm_OneTransferHasOneMove,
.B.RROAT = dma_chcfgrxxx_rroat_ResetAfterEachTransfer,
.B.CHMODE = dma_chcfgrxxx_chmode_SingleMode,
.B.CHDW = dma_chcfgrxxx_chdw_DataBitWidth_8,
.B.PATSEL = dma_chcfgrxxx_patsel_NoPatternCompareOperation,
.B.PRSEL = dma_chcfgrxxx_prsel_HardwareRequestSelected,
.B.DMAPRIO = dma_chcfgrxxx_dmaprio_LowPrioritySelected,
};
DMA_CHCFGR006.U = chcfgr_6.U;

Ifx_DMA_CH_ADICR adicr_6 = {
.B.SMF = dma_adicrxxx_smf_AddressOffsetIsCHDWx1,
.B.INCS = dma_adicrxxx_incs_AddressOffsetIsSubtracted,
.B.DMF = dma_adicrxxx_dmf_AddressOffsetIsCHDWx1,
.B.INCD = dma_adicrxxx_incd_AddressOffsetIsAdded,
.B.CBLS = dma_adicrxxx_cbls_SourceAddress31to0,
.B.CBLD = dma_adicrxxx_cbld_DestinationAddress31to0,
.B.SHCT = dma_adicrxxx_shct_MoveOperation,
.B.SCBE = dma_adicrxxx_scbe_SourceCircularBufferEnabled,
.B.DCBE = dma_adicrxxx_dcbe_DestinationCircularBufferDisabled,
.B.STAMP = dma_adicrxxx_stamp_NoAction,
.B.ETRL = dma_adicrxxx_etrl_NoInterruptOnLostEvent,
.B.WRPSE = dma_adicrxxx_wrpse_WrapSourceBufferInterruptTriggerDiabled,
.B.WRPDE = dma_adicrxxx_wrpde_WrapDestinationBufferInterruptTriggerDiabled,
.B.INTCT =dma_adicrxxx_intct_InterruptChangingTCOUNTandEqualsIRDV,
.B.IRDV = 0, /*Interrupt Raise Detect Value*/
};
DMA_ADICR006.U = adicr_6.U;
DMA_TSR006.B.ECH = 1;

/* Setup interrupt from DMA Channel 5 and 6 when the Move Transaction has finished */
SRC_DMACH5.U = (CPU0_SERVICE << TOS) | (1 << SRE) | INTPRIO_DMA_CH5;
SRC_DMACH6.U = (CPU0_SERVICE << TOS) | (1 << SRE) | INTPRIO_DMA_CH6;
}


I think it would be too much work to rewrite the whole QSPI module.
I'm planning on rewriting only IfxQspi_SpiMaster_write() since this is the worst code in the whole module, and the source of most of the problems.
Thanks for the help, though.

Toshi
0 Likes
User16286
Level 4
Level 4
First like received
I've rewritten IfxQspi_SpiMaster_write() and running in to a problem.

The test code tries to send one byte via SPI DMA.
It loads the BACON into qspiSFR->BACONENTRY.U before triggering the DMA.
However, as soon as it loads the BACON, the SPI sends a byte, which I don't think it should do.
What am I missing?

Toshi
0 Likes
User16286
Level 4
Level 4
First like received
Ah, never mind.
The DMA is starting as soon as the BACON is pulled from the TXFIFO.
0 Likes
User16286
Level 4
Level 4
First like received
From what I can tell, there is an endianness incompatibility problem between the QSPI and DMA modules.
There is a QSPI feature ECON.BE that tries to "swap endianness" of the incoming data, but it appears to be broken.
Here's an example:

Given the byte stream 0x00 0x01 0x02 0x03 loaded via 32-bit DMA into the TXFIFO:

o ECON.BE = 0 (with BACON.BYTE = 0) will transmit:

32 bits: 0x03 0x02 0x01 0x00
24 bits: 0x02 0x01 0x00
16 bits: 0x01 0x00
8 bits: 0x00

o ECON.BE = 1 (with BACON.BYTE = 0) will transmit:

32 bits: 0x02 0x03 0x00 0x01
24 bits: 0x00 0x00 0x01
16 bits: 0x00 0x01
8 bits: 0x00

o ECON.BE = 2 (with BACON.BYTE = 0) will transmit:

32 bits: 0x00 0x01 0x02 0x03
24 bits: 0x01 0x02 0x00
16 bits: 0x00 0x00
8 bits: 0x00

None of the valid ECON.BE settings will send 24 bits as 0x00 0x01 0x02.

Anyone else notice this problem?

Toshi
0 Likes
NeMa_4793301
Level 6
Level 6
10 likes received 10 solutions authored 5 solutions authored
Not surprising, from this note in the User Manual:
The endianness byte permutation should be used only for 16-bit and 32-bit data (and multiples of it), and not for other data lengths.
0 Likes
User16286
Level 4
Level 4
First like received
UC_wrangler wrote:
Not surprising, from this note in the User Manual:
The endianness byte permutation should be used only for 16-bit and 32-bit data (and multiples of it), and not for other data lengths.


Ah, they documented the bug and made it a feature.

Toshi

May i know how you tried to implement queue functionality in your code for multiple channels

0 Likes