Enabling SPI Done in SCB INTR_M_MASK

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

cross mob
CaKu_4284131
Level 5
Level 5
50 replies posted 25 replies posted 10 likes received

In PSoC 6 MCU with BLE: CY8C63x6, CY8C63x7 Architecture TRM, Document No. 002-18176 Rev. *H, section 25.6 SCB Interrupts, it says: "Note: To avoid being triggered by events from previous transactions, whenever the firmware enables an interrupt mask register bit, it should clear the interrupt request register in advance." I'm not sure what they are referring by "interrupt request register ." Would that be INTR_M?

In my mainline code, I have 

 

/* Unmasking only the spi done interrupt bit */
this->spi->base->INTR_M_MASK = SCB_INTR_M_SPI_DONE_Msk;

 

and in my ISR, 

 

	/* Mask the spi done interrupt bit */
	this->base->INTR_M_MASK &= ~SCB_INTR_M_SPI_DONE_Msk;

 

Is the Architecture TRM saying that before I unmask the SPI Done bit in INTR_M_MASK I should clear INTR_M? 

From PSoC 6 MCU: CY8C61x6, CY8C61x7 Registers TRM, Document Number: 002-23456 Rev. *C 1255, it looks like the SPI_DONE bit is RW1C. Should I do something like

 

	 /* Unmasking only the spi done interrupt bit */
    this->spi->base->INTR_M = SCB_INTR_M_SPI_DONE_Msk; // Clear request register; RW1C    
	this->spi->base->INTR_M_MASK = SCB_INTR_M_SPI_DONE_Msk;        

 

That doesn't seem quite right, because I'm only clearing one bit, but the TRM says I should "clear the interrupt request register." 

 

0 Likes
1 Solution
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @CaKu_4284131 , The SPI Done interrupt is triggered every time the all the bytes loaded in the TX FIFO is sent out and is empty. If the DMA is preempted/ blocked and if this caused the TX FIFO to be emptied, then the SPI done bit is likely to be set. Can you verify this by probing the CS line and check if it is deasserted in between transactions whenever you receive such extra interrupts.

a. You can try to make the DMA the highest priority channel and ensure that it is not preempted.

b. There could also be bus arbitration loss that might cause the DMA to be pushed in wait cycles. You can increase the priority of the DMA (AHB bus master) higher than the CPU so that DMA doesn't lose the arbitration to the CPU or other bus masters in the AHB bus.

Another way is to use the DMA complete interrupt to know the transfer complete status of SPI instead of the SPI done bit.

Regards,
Bragadeesh

View solution in original post

0 Likes
6 Replies
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @CaKu_4284131 ,

The note means that before enabling the interrupt using the mask register (INTR_M_MASK), you need to clear the corresponding interrupt request register (INTR_M) to prevent it from falsely triggering.

For example, let us say that you hadn't enabled the SPI DONE interrupt previously in firmware. Assume that after the previous SPI transaction is complete, the interrupt request register would set the SPI done flag.

Note: Remember that the Interrupt request register will set flags irrespective of whether you have enabled the interrupt in the interrupt mask register or not . The mask register will just determine if the interrupt will trigger the NVIC and hence the interrupt service routine.  

After this, if you enable the SPI done interrupt using the mask register without clearing the present status of the interrupt request register, you will observe the SPI DONE interrupt firing as soon as you enable the mask. This is not desired. This is why it is recommended to clear the interrupt request register before setting the interrupt mask register.

Is there any specific reason why you are trying to directly write into the register in a bit level? I would recommend you to use the PDL that is developed and tested to do this. For example, to achieve this, you can write the following lines of code:

Cy_SCB_SPI_ClearSlaveMasterStatus(SPI_HW, CY_SCB_SPI_MASTER_DONE) // This line clears the SPI done bit alone in the interrupt request register (INTR_M)

Cy_SCB_SetMasterInterruptMask(SPI_HW, CY_SCB_MASTER_INTR_SPI_DONE) // Sets the SPI done bit alone in the interrupt mask register. (INTR_M_MASK)

It is enough to clear only the bits in the interrupt request register that you are going to enable in the interrupt mask register. It is not required to clear the entire interrupt request register.

Also, I do not understand why you are unmasking the bit in the ISR? Is that the requirement to disable the interrupt in the iSR? If your intention is to clear the ISR, then you need to call the following lines of Code in the ISR

if(Cy_SCB_SPI_GetSlaveMasterStatus(SPI_HW) & CY_SCB_SPI_MASTER_DONE != 0u) //Find who caused the interrupt - (INTR_M_MASKED)

{

         Cy_SCB_SPI_ClearSlaveMasterStatus(SPI_HW, CY_SCB_SPI_MASTER_DONE); // Clear the interrupt 

}

Regards,
Bragadeesh
0 Likes

@BragadeeshV wrote:

Hi @CaKu_4284131 ,

The note means that before enabling the interrupt using the mask register (INTR_M_MASK), you need to clear the corresponding interrupt request register (INTR_M) to prevent it from falsely triggering.

For example, let us say that you hadn't enabled the SPI DONE interrupt previously in firmware. Assume that after the previous SPI transaction is complete, the interrupt request register would set the SPI done flag.

Note: Remember that the Interrupt request register will set flags irrespective of whether you have enabled the interrupt in the interrupt mask register or not . The mask register will just determine if the interrupt will trigger the NVIC and hence the interrupt service routine.  

After this, if you enable the SPI done interrupt using the mask register without clearing the present status of the interrupt request register, you will observe the SPI DONE interrupt firing as soon as you enable the mask. This is not desired. This is why it is recommended to clear the interrupt request register before setting the interrupt mask register.

Got it. Thanks!

Is there any specific reason why you are trying to directly write into the register in a bit level? I would recommend you to use the PDL that is developed and tested to do this. For example, to achieve this, you can write the following lines of code:

Cy_SCB_SPI_ClearSlaveMasterStatus(SPI_HW, CY_SCB_SPI_MASTER_DONE) // This line clears the SPI done bit alone in the interrupt request register (INTR_M)

Cy_SCB_SetMasterInterruptMask(SPI_HW, CY_SCB_MASTER_INTR_SPI_DONE) // Sets the SPI done bit alone in the interrupt mask register. (INTR_M_MASK)

It is enough to clear only the bits in the interrupt request register that you are going to enable in the interrupt mask register. It is not required to clear the entire interrupt request register.

I started with the "CE219656 - PSoC® 6 MCU UART using Low Level APIs" code sample, which directly manipulates the bits. But, I have now changed over to the 

Cy_SCB_SPI_ClearSlaveMasterStatus(this->spi->base, CY_SCB_SPI_MASTER_DONE); 
/* Unmasking only the spi done interrupt bit */ 
Cy_SCB_SetMasterInterruptMask(this->spi->base, SCB_INTR_M_SPI_DONE_Msk);

calls.

Also, I do not understand why you are unmasking the bit in the ISR? Is that the requirement to disable the interrupt in the iSR? If your intention is to clear the ISR, then you need to call the following lines of Code in the ISR

if(Cy_SCB_SPI_GetSlaveMasterStatus(SPI_HW) & CY_SCB_SPI_MASTER_DONE != 0u) //Find who caused the interrupt - (INTR_M_MASKED)

{

         Cy_SCB_SPI_ClearSlaveMasterStatus(SPI_HW, CY_SCB_SPI_MASTER_DONE); // Clear the interrupt 

}


I now have this in the ISR:

void spi_ISR(spi_t *this) {
	/* Check the status of master */    
    if (CY_SCB_MASTER_INTR & Cy_SCB_GetInterruptCause(this->base) 
    && CY_SCB_SPI_MASTER_DONE == Cy_SCB_SPI_GetSlaveMasterStatus(this->base)) {

        Cy_SCB_SPI_ClearSlaveMasterStatus(this->base, CY_SCB_SPI_MASTER_DONE);    
        
    	/* Mask the spi done interrupt bit */
    	//this->base->INTR_M_MASK &= ~SCB_INTR_M_SPI_DONE_Msk;
        //Cy_SCB_SetMasterInterruptMask(this->base, ~SCB_INTR_M_SPI_DONE_Msk & Cy_SCB_GetMasterInterruptMask(this->base));
        Cy_SCB_SetMasterInterruptMask(this->base, 0);
 //...

I was getting extraneous interrupts until I put in the masking. Cy_SCB_SPI_ClearSlaveMasterStatus by itself didn't seem to be sufficient. For a while I ran with only Cy_SCB_SetMasterInterruptMask, which seemed to work, but did give me some spurious interrupts.  So now I do both.

The mainline code does:

	
    Cy_SCB_SPI_ClearSlaveMasterStatus(this->spi->base, CY_SCB_SPI_MASTER_DONE);    
	/* Unmasking only the spi done interrupt bit */
	Cy_SCB_SetMasterInterruptMask(this->spi->base, SCB_INTR_M_SPI_DONE_Msk);
	
	uint32_t n = Cy_SCB_SPI_Write(this->spi->base, value);

Thanks,

     Carl

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @CaKu_4284131 , You can use the following link, which has reference to the older version of PSoC 6 SPI master example using low level PDL APIs.

https://github.com/cypresssemiconductorco/Code-Examples-for-the-ModusToolbox-PSoC-6-SDK/tree/mt1.1/C...

Please note that this example is not compatible with MTB 2.x and is only shared for reference.

Note that they have not cleared the masterstatus before enabling the SPI mask since this is the first time we are masking the bit and the interrupt bits are cleared by default in the init functions. However, while you mask this interrupt bit elsewhere you need to clear the status first.

Please let us know if you have questions.

Regards,
Bragadeesh
0 Likes

In my code, just clearing MasterStatus by itself in the ISR still left me with extraneous interrupts. This is complicated by the fact that I'm using DMA in some cases. Is the Done interrupt driven every time the FIFO empties? Maybe the DMA isn't keeping up with the SPI? When I  use DMA I also wait for a second,  SPI RxDmaComplete interrupt.

0 Likes
BragadeeshV
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hi @CaKu_4284131 , The SPI Done interrupt is triggered every time the all the bytes loaded in the TX FIFO is sent out and is empty. If the DMA is preempted/ blocked and if this caused the TX FIFO to be emptied, then the SPI done bit is likely to be set. Can you verify this by probing the CS line and check if it is deasserted in between transactions whenever you receive such extra interrupts.

a. You can try to make the DMA the highest priority channel and ensure that it is not preempted.

b. There could also be bus arbitration loss that might cause the DMA to be pushed in wait cycles. You can increase the priority of the DMA (AHB bus master) higher than the CPU so that DMA doesn't lose the arbitration to the CPU or other bus masters in the AHB bus.

Another way is to use the DMA complete interrupt to know the transfer complete status of SPI instead of the SPI done bit.

Regards,
Bragadeesh
0 Likes

Thanks. What I'm doing now, and it seems to work well, is to mask off the SPI interrupt for DMA transfers, and only wait for a DMA RX complete interrupt (on the theory that if the RX is complete, so must be the TX).

0 Likes