TC234 QSPI interrupts not working

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 get tx/rx interrupts working on QSPI channel 1.
The bits seem to be sent properly from the Tricore to the slave, and the slave is sending data back ok.
However, the interrupts don't seem to be working properly.
I'm trying to get rx interrupts working first.
In the registers I see:

QSPI1_GLOBALCON1.RXFM = 1 (single move mode)
QSPI1_GLOBALCON1.RXEN = 1 (rx interrupt enabled)

after running the code, I see:

QSPI1_STATUS.ERRORFLAGS = 0x20 (rxfifo overflow)
QSPI1_STATUS.RXF = 1
QSPI1_STATUS.RXFIFOLEVEL = 4
INT_LWSR.STAT = 0

So my interpretation of this is:

o The QSPI RX is receiving data
o The RX FIFO is receiving the data
o The QSPI thinks it has generated an interrupt
o RX FIFO has overflowed because software did not read any data

I've checked the SRC registers, and it looks like they are set correctly.
The vector table entry for the interrupt number also looks correct.
Am I missing any configuration bits which need to be set or cleared?

Toshi
0 Likes
13 Replies
NeMa_4793301
Level 6
Level 6
10 likes received 10 solutions authored 5 solutions authored
Hi Toshi. The Interrupt Router registers will also be part of the picture. Can you check what's in SRC_QSPI1RX? Have you configured TOS for the right CPU? Is the interrupt pending?

Also, are interrupts enabled on the CPU (ICR.IE=1)?
0 Likes
User16286
Level 4
Level 4
First like received
UC_wrangler wrote:
Hi Toshi. The Interrupt Router registers will also be part of the picture. Can you check what's in SRC_QSPI1RX? Have you configured TOS for the right CPU? Is the interrupt pending?

Also, are interrupts enabled on the CPU (ICR.IE=1)?


I've already checked the SRC registers, and they appear to be correct.
ICR.IE=1 yes.
Any other bits I should check?
0 Likes
User16286
Level 4
Level 4
First like received
UC_wrangler wrote:
Hi Toshi. The Interrupt Router registers will also be part of the picture. Can you check what's in SRC_QSPI1RX? Have you configured TOS for the right CPU? Is the interrupt pending?

Also, are interrupts enabled on the CPU (ICR.IE=1)?


The SRC values are:

SRC_QSPI1RX.SWSCLR = 0
SRC_QSPI1RX.IOV = 0
SRC_QSPI1RX.SRR = 0
SRC_QSPI1RX.SRE = 1
SRC_QSPI1RX.SWS = 0
SRC_QSPI1RX.SETR = 0
SRC_QSPI1RX.ECC = 0x18
SRC_QSPI1RX.SRPN = 0x3d
SRC_QSPI1RX.IOVCLR = 0
SRC_QSPI1RX.CLRR = 0
SRC_QSPI1RX.TOS = 0

My interpretation of this is:

o SRC claims the QSPI1 RX has never been triggered

So it looks like QSPI1 and SRC disagree on whether the interrupts have been triggered.
0 Likes
cwunder
Employee
Employee
5 likes given 50 likes received 50 solutions authored
At first pass you appear to have everything set correctly. What toolchain are you using?
If you put a breakpoint on your RX ISR and manually trigger the interrupt (SRCn.SETR = 1) do you go to the interrupt?

I don't use the illDs but here is my code for the QSPI3 (Master on the TC277 which would be the same for the TC234) if it helps using the HighTec free toolchain.


void QSPI3_Init(void)
{
uint16_t cpuWdtPassword = IfxScuWdt_getCpuWatchdogPassword();


IfxScuWdt_clearCpuEndinit(cpuWdtPassword);
QSPI3_CLC.U = 0x0; /* enable peripheral and allow sleep mode */
(void) QSPI3_CLC.U; /* read the register to force the write out of the cpu */


/* Kernel reset */
QSPI3_KRST0.U = 0x01;
QSPI3_KRST1.U = 0x01;
IfxScuWdt_setCpuEndinit(cpuWdtPassword);


/* wait for the kernel to be reset before proceeding */
while((QSPI3_KRST0.U & 0x02) != 0x02);


IfxScuWdt_clearCpuEndinit(cpuWdtPassword);
QSPI3_KRSTCLR.U = 0x01; /* Clear Reset done flag */
IfxScuWdt_setCpuEndinit(cpuWdtPassword);


/*
* The timing of the QSPI parameters are dependent on the input clock
* fBUAD2 is running at 200MHz
*/


/* GLOBALCON contains configuration parameters which affect all channels */
const Ifx_QSPI_GLOBALCON globalcon =
{
.B.TQ = 8, /* Global DIVIDER VALUE IS (TQ+1) */
.B.SI = qspi_globalcon_si_Disabled, /* Status Injection */
.B.EXPECT = qspi_globalcon_expect_1024, /* Time-Out Value for the Expect Phase */
.B.LB = qspi_globalcon_lb_Active, /* Loop-Back Control */
.B.DEL0 = qspi_globalcon_del0_Off, /* Delayed Mode for SLSO0 */
.B.STROBE = qspi_globalcon_strobe_NotUsed, /* Strobe Delay for SLSO0 in Delayed Mode */
.B.SRF = qspi_globalcon_srf_disabled, /* Stop on RxFIFO Full */
.B.STIP = qspi_globalcon_stip_NotUsed, /* Slave Transmit Idle State Polarity */
.B.EN = qspi_globalcon_en_PAUSE, /* Enable Bit */
.B.MS = qspi_globalcon_ms_MasterTransmitReceive, /* Master or Slave Mode */
.B.AREN = qspi_globalcon_aren_AutomaticResetEnabled, /* Automatic Reset Enable */
.B.RESETS = qspi_globalcon_resets_default /* Bits for resetting sub-modules per software */
};
QSPI3_GLOBALCON.U = globalcon.U;


/* GLOBALCON1 contains bit fields for control of the extension of the slave select signals by using an external demultiplexer */
const Ifx_QSPI_GLOBALCON1 globalcon1 =
{
.B.ERRORENS = qspi_globalcon1_errorens_AllErrorsEnabled, /* Error Enable Bits */
.B.TXEN = qspi_globalcon1_txen_InterruptEnabled, /* TX Interrupt Event Enable */
.B.RXEN = qspi_globalcon1_rxen_InterruptEnabled, /* RX Interrupt Event Enable */
.B.PT1EN = qspi_globalcon1_pt1en_InterruptDisabled, /* Interrupt on PT1 Event Enable */
.B.PT2EN = qspi_globalcon1_pt2en_InterruptDisabled, /* Interrupt on PT2 Event Enable */
.B.USREN = qspi_globalcon1_usren_InterruptDisabled, /* Interrupt on USR Event Enable */
.B.TXFIFOINT = qspi_globalcon1_txfifoint_Level2, /* Transmit FIFO Interrupt Threshold */
.B.RXFIFOINT = qspi_globalcon1_rxfifoint_Level1, /* Receive FIFO Interrupt Threshold */
.B.PT1 = qspi_globalcon1_pt1_EndOfWaitPhase, /* Phase Transition Event 1 */
.B.PT2 = qspi_globalcon1_pt2_EndOfWaitPhase, /* Phase Transition Event 2 */
.B.TXFM = qspi_globalcon1_txfm_SingleMoveMode, /* TXFIFO Mode */
.B.RXFM = qspi_globalcon1_rxfm_SingleMoveMode, /* RXFIFO Mode */
};
QSPI3_GLOBALCON1.U = globalcon1.U;


/* Defines the basic configuration parameters for the current slave select. It can be read by
* software, or written through a write to the TXFIFO.
*/
const Ifx_QSPI_BACON baconEntry =
{
.B.CS = 12, /* Slave Channel Select for timing */
.B.DL = 14, /* value = Data Length + 1 data bits */
.B.BYTE = qspi_baconEntry_byte_Bits, /* length in bits or bytes */
.B.MSB = qspi_baconEntry_Shift_LSB_first, /* Shift Mode */
.B.UINT = qspi_baconEntry_UserInterruptPT1_EventDisabled, /* User Interrupt at the PT1 Event in the Subsequent Frames */
.B.PARTYP = qspi_baconEntry_partyp_Even, /* Parity Type */
.B.TRAIL = 3, /* 2 Trailing Delay Length*/
.B.TPRE = 1, /* 3 Prescaler for the Trailing Delay */
.B.LEAD = 3, /* 2 Leading Delay Length */
.B.LPRE = 1, /* 3 Prescaler for the Leading Delay */
.B.IDLE = 0, /* 7 Idle Delay Length */
.B.IPRE = 5, /* 3 Prescaler for the Idle Delay */
.B.LAST = qspi_baconEntry_last_true, /* Last Word in a Frame */
};
QSPI3_BACONENTRY.U = baconEntry.U;


const Ifx_QSPI_ECON econ =
{
.B.Q = 3, /* DIVIDER VALUE IS (Q+1) */
.B.A = 1, /* DIVIDER VALUE IS (A+1) */
.B.B = 1, /* Length expressed in time quantum of ECONz.Q */
.B.C = 1, /* Length expressed in time quantum of ECONz.Q */
.B.CPH = qspi_econ_cph_0, /* Clock Phase (mandatory setting) */
.B.CPOL = qspi_econ_cpol_low, /* Clock Polarity (mandatory setting) */
.B.PAREN = qspi_econ_paren_Enabled, /* Enable Parity Check */
.B.BE = qspi_econ_be_SwapDisabled, /* Permutate bytes to / from Big Endian */
};
QSPI3_ECON4.U = econ.U;


const Ifx_QSPI_SSOC ssoc =
{
.B.OEN = qspi_ssoc_oen_slso12,
.B.AOL = qspi_ssoc_aol_none,
};
QSPI3_SSOC.U = ssoc.U;


const Ifx_QSPI_STATUS1 status1 =
{
.B.SPD = qspi_status1_SpikeDetectionNoEvent, /* Spike Detection Flag */
.B.SPDEN = qspi_status1_SpikeDetectionDisabled, /* Spike Detection Enable */
.B.BRD = qspi_status1_BaudRateDeviationNoEvent, /* Baud Rate Deviation Flag */
.B.BRDEN = qspi_status1_BaudRateDeviationDisabled, /* Baud Rate Deviation Enable */
};
QSPI3_STATUS1.U = status1.U;

#if 1 // use interrupt or polling
#if 0 // polling
/* Enter interrupt level and initialize both the SRC and table entry */
SRC_QSPI3RX.U = (CPU0_SERVICE << TOS) | (false << SRE) | INTPRIO_CPU0_QSPI3_RX;
SRC_QSPI3TX.U = (CPU0_SERVICE << TOS) | (false << SRE) | INTPRIO_CPU0_QSPI3_TX;
#else // interrupts
/* Enter interrupt level and initialize both the SRC and table entry */
SRC_QSPI3RX.U = (CPU0_SERVICE << TOS) | (true << SRE) | INTPRIO_CPU0_QSPI3_RX;
SRC_QSPI3TX.U = (CPU0_SERVICE << TOS) | (true << SRE) | INTPRIO_CPU0_QSPI3_TX;
#endif
#else // use DMA
SRC_QSPI3RX.U = (DMA_SERVICE << TOS) | (true << SRE) | DMA_CH6_QSPI3_RX;
SRC_QSPI3TX.U = (DMA_SERVICE << TOS) | (true << SRE) | DMA_CH5_QSPI3_TX;
#endif

IfxScuWdt_clearCpuEndinit(cpuWdtPassword);
/* select pins for the QSPI channel */
const Ifx_QSPI_PISEL pisel =
{
.B.MRIS = qspi_pisel_mris_mrstxE,
.B.SRIS = qspi_pisel_sris_mtsrxA,
.B.SCIS = qspi_pisel_scis_sclkxA,
.B.SLSIS = qspi_pisel_slsis_slsi_deselected,
};
QSPI3_PISEL.U = pisel.U;

/*
* P22.0 QSPI3 Master Transmit Output
* P22.1 QSPI3 Master Receive Input E
* P22.2 QSPI3 Slave Select Output 12 (ECON4)
* P22.3 QSPI3 Master Clock Output
*/
P22_IOCR0.B.PC0 = Port_PCx_Output_PushPull_Alt_3;
P22_IOCR0.B.PC1 = Port_PCx_Input_PullDown;
P22_IOCR0.B.PC2 = Port_PCx_Output_PushPull_Alt_3;
P22_IOCR0.B.PC3 = Port_PCx_Output_PushPull_Alt_3;

// Set the driver strength
P22_PDR0.U = Port_PDx_Speed_Grade_1;
IfxScuWdt_setCpuEndinit(cpuWdtPassword);

/* clear any pending interrupt */
SRC_QSPI3TX.B.CLRR = true;

/* Enable SPI with user configuration */
QSPI3_GLOBALCON.B.EN = qspi_globalcon_en_RUN;

/* set up data structures */
QSPI3_txHeadIdx = 0;
QSPI3_txTailIdx = 0;
QSPI3_rxHeadIdx = 0;
QSPI3_rxTailIdx = 0;
QSPI3_txSendCount = 0;
QSPI3_rxReceiveCount = 0;
}


/** \brief Service the Receive interrupt on the QSPI
*
* \param None
* \return Nothing
*/
IFX_INTERRUPT(QSPI3_RxISR, 0, INTPRIO_CPU0_QSPI3_RX);
void QSPI3_RxISR(void)
{
/* indicated to the software loop we have data waiting in the software receive buffer */
QSPI3_rxReceiveCount++;


/* read received data into the ring buffer clearing the CMD bit */
QSPI3_rxBuf[QSPI3_rxHeadIdx++] = (uint16_t) (QSPI3_RXEXIT.U);


/* increment the head pointer of the receive ring buffer */
QSPI3_rxHeadIdx = QSPI3_rxHeadIdx & (QSPI3_RXBUF_SIZE - 1);
}




/** \brief Service the Transmit interrupt on the QSPI
*
* \param None
* \return Nothing
*/
IFX_INTERRUPT(QSPI3_TxISR, 0, INTPRIO_CPU0_QSPI3_TX);
void QSPI3_TxISR(void)
{
/* Transmit interrupt flag handler */
if (QSPI3_txSendCount > 0)
{
QSPI3_txSendCount--;
QSPI3_DATAENTRY0.U = QSPI3_txBuf[QSPI3_txTailIdx++];
QSPI3_txTailIdx = QSPI3_txTailIdx & (QSPI3_TXBUF_SIZE - 1);
}
else
{
QSPI3_txBusy = false;
}
}


0 Likes
User16286
Level 4
Level 4
First like received
I'm using Hightech with the PLS debugger.
That's a good idea. I put this in my code:

SRC_QSPI1RX.B.SETR = 1;

and single-stepped it.
I see these bits change:

SRC_QSPIRX.SRR = 1
SRC_QSPIRX.SWS = 1

and it doesn't call the interrupt handler.

So it seems there is another problem - something is wrong with the vector table.

Toshi
0 Likes
User16286
Level 4
Level 4
First like received
One odd thing I noticed:

When the code reaches the statement:

SRC_QSPI1RX.B.SETR = 1;

The CPU0_ICR.PIPN is already 0x3c.
This seems odd.
Is CPU0_IRC.PIPN supposed to be nonzero in mainline code?
It looks like the CPU thinks it's in an interrupt handler or something.
0 Likes
cwunder
Employee
Employee
5 likes given 50 likes received 50 solutions authored
The CPU will start at level 0 (default) and won't initiate an interrupt service. TriCore doesn't have a fixed vector table you can place entries based on their priority (higher the number the greater the priority).
The CPU will only vector to an interrupt if it is greater than the current priority level.

What interrupt vector entry is 0x3C? Anyhow 0x3D should vector as it has a higher priority. Where is the PC?

If you break at core0_main you should see CPU0_ICR.PIPN at zero. What do you have?
0 Likes
User16286
Level 4
Level 4
First like received
#define ISR_PRIORITY_QSPI1_TX 60
#define ISR_PRIORITY_QSPI1_RX 61
#define ISR_PRIORITY_QSPI1_ER 62

So 0x3c is the QSPI1 TX interrupt.

Not sure what you mean by "Where is the PC?".
It's on the SRC_QSPI1RX.B.SETR = 1; statement when I check the registers.

yes, CPU0_ICR.PIPN is zero at main() entry.

What I really want to know is: is it okay for CPU0_ICR.PIPN to be nonzero in mainline code?
My mental model of the behavior of PIPN is I expect it to be set when it enters the interrupt handler, and to be cleared to zero (in hardware) when it exits the interrupt handler.
If this is true, then PIPN having a nonzero value in the mainline code means an interrupt was masked.
If PIPN is not cleared by hardware on interrupt handler exit, then it's okay for PIPN to be nonzero in mainline code.
0 Likes
cwunder
Employee
Employee
5 likes given 50 likes received 50 solutions authored
The CPU0_ICR.PIPN would start with zero. The requirement is that any SRC must be greater than zero for the CPU to vector. Your understanding is basically correct however you can have nested interrupts so returning from an interrupt will restore you the previous level. If it was zero then it would be zero.

What I really want to know is: is it okay for CPU0_ICR.PIPN to be nonzero in mainline code?
Yes
If this is true, then PIPN having a nonzero value in the mainline code means an interrupt was masked.

No, you can interrupt if any higher priority level provided global interrupts are still enabled. Note when you vector to an interrupt the global interrupts are disabled by hardware. You need to re-enable them in the ISR to if you want nested interrupts.
I would put a break point in the Tx ISR and step through this to see if the Program Counter is returning to your user code not the ISR code.
0 Likes
User16286
Level 4
Level 4
First like received
cwunder wrote:
The CPU0_ICR.PIPN would start with zero. The requirement is that any SRC must be greater than zero for the CPU to vector. Your understanding is basically correct however you can have nested interrupts so returning from an interrupt will restore you the previous level. If it was zero then it would be zero.

Yes

No, you can interrupt if any higher priority level provided global interrupts are still enabled. Note when you vector to an interrupt the global interrupts are disabled by hardware. You need to re-enable them in the ISR to if you want nested interrupts.
I would put a break point in the Tx ISR and step through this to see if the Program Counter is returning to your user code not the ISR code.


The issue is I am not getting SPI Tx or Rx interrupts.
So I can put a breakpoint in the Tx ISR, but it will never break, because the code is never executed.

Toshi
0 Likes
User16286
Level 4
Level 4
First like received
I think I just realized the problem.
The CPU0_BIV is 0x80030000 which doesn't look right for IFX_USE_SW_MANAGED_INT.
This project was originally started on TASKING, and then I moved to Hightec because TASKING wouldn't compile lwIP properly.
When I moved the project, apparently I didn't port the interrupt code properly.
I'll fix the linker script, and hopefully that will fix the problem.

Toshi
0 Likes
cwunder
Employee
Employee
5 likes given 50 likes received 50 solutions authored
The CPU0_ICR.PIPN is already 0x3c.
This seems odd.
Is CPU0_IRC.PIPN supposed to be nonzero in mainline code?
It looks like the CPU thinks it's in an interrupt handler or something.

As you stated this is your Transmit interrupt. This can occur in the initialization of the QSPI if you are writing to the TXFIFO, for example writing to the BACON_ENTRY.
Assuming you don't want this event to be serviced you could add code to clear the TXC flag in the QSPI before writing your configuration to the SRC_QSPInTX register (where you also must have the CLRR bit set).
The CPU0_BIV is 0x80030000 which doesn't look right for IFX_USE_SW_MANAGED_INT.

This is not correct when using software managed interrupts. The interrupt vector table will OR the entry from the base address to get the offset. When using this mode you only have one entry in the vector table and bits 10..3 must be all ones. This one ISR then uses the CCPN to get the offset (address) of the function to call (your ISR). You can find more information on this in section 16.9.1 of the TC23x users manual (note the diagrams should should show the high bit of bit 10 not 9)

Concerning the PIPN should be zero if there are no pending interrupts. This is an errata
CPU_TC.127 Pending Interrupt Priority Number PIPN in Register ICRIn the TriCore Architecture Manual, it is described for the Pending Interrupt Priority Number ICR.PIPN that it is reset to 0x0 in case there is no request pending.
However, the AURIX™ hardware implementation behaves differently, as the value of PIPN is not changed after the interrupt is serviced in case there is no further request pending.
0 Likes
User16286
Level 4
Level 4
First like received
I fixed the interrupt handler and everything is working.
Thanks for the help!

Toshi
0 Likes