Skip to main content
Gpeti
Senior
June 8, 2020
Question

STM32H7 SPI communication issue: RxFIFO management

  • June 8, 2020
  • 12 replies
  • 4006 views

I've been struggling for quite a while now on my SPI setup.

The setup is as follows:

- SPI Slave is a Nucleo STM32H743 in simplex mode, clocks at maximum (sysclk 400MHz, hclck 200Mhz, APB clock 100MHz)

- SPI master is another identical Nucleo in simplex mode, clocks divided by 2: sysclk 200MHz, etc... and spi_ker_clk = 100MHz

- SPI master with a prescaler of 16 ie. clock SPI around 6MHz. CRC enabled. Data frame 8 bits. FIFO threshold 4 bytes

- SPI slave: CRC enabled, 8 bits. FIFO threshold 4 bytes

There is no Slave Select signal to control the slave.

Here is the code for the master. Everything done in polling and I've added some delay to let time to the Slave to work. The following function is called in loop, nothing else is done by the Master (in this simplified version that I used for debug):

  

uint32_t SPI_EnvoiCommandeTest(void)
 
 {
 
 uint32_t resp;
 
 uint8_t statut;
 
 
 SPI1->CFG2 |= SPI_CFG2_COMM_0;
 
 SPI1->CFG2 &= ~SPI_CFG2_COMM_1;
 
 
 SPI1->CR2 = 4;
 
 
 SPI1->CR1 |= SPI_CR1_SPE;
 
 SPI1->CR1 |= SPI_CR1_CSTART;
 
 
 SPI1->TXDR = 0x12345678;
 
 
 while ( (SPI1->SR & SPI_SR_EOT) == 0 );
 
 
 if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
 
 {
 
 return ( SPI1->SR & SPI_SR_ERR_MASK);
 
 }
 
 
 
 SPI1->IFCR = 0xFFFFFFFF;
 
 
 
 SPI1->CR1 &= ~SPI_CR1_SPE;
 
 
 Delay(1000); 
 
 
 
 SPI1->CFG2 |= SPI_CFG2_COMM_1;
 
 SPI1->CFG2 &= ~SPI_CFG2_COMM_0;
 
 
 
 SPI1->CR2 = 5;
 
 
 
 SPI1->CR1 |= SPI_CR1_SPE;
 
 SPI1->CR1 |= SPI_CR1_CSTART;
 
 
 while ( (SPI1->SR & SPI_SR_EOT) == 0 );
 
 resp = SPI1->RXDR;
 
 statut = *((__IO octet *)&(SPI1->RXDR));
 
 if ( resp != 0x9ABCDEFF)
 
 while(1);
 
 if ( statut != 0x77)
 
 while(1);
 
 
 while ( (SPI1->SR & SPI_SR_EOT) == 0 );
 
 
 if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
 
 {
 
 return ( SPI1->SR & SPI_SR_ERR_MASK);
 
 }
 
 
 
 SPI1->IFCR = 0xFFFFFFFF;
 
 
 
 SPI1->CR1 &= ~SPI_CR1_SPE;
 
 
 
 Delay(1000); 
 
 
 
 return 0;
 
 }

For the Slave, the reception is done by the interrupt handler. The main thread is just waiting for a flag to be set (set by `SPI_StopReception()`) and send 5 bytes of answer.

 

 static void SPI_GenericHandler(SpiId_e SpiId)
 
 {
 
 SPI_TypeDef *Spi = SpiMgt[SpiId].SpiInstance;
 
 uint32_t trigger = Spi->IER & Spi->SR;
 
 uint32_t cmd;
 
 uint8_t stat;
 
 
 if (trigger & SPI_SR_RXP)
 
 {
 
 
 
 cmd = Spi->RXDR;
 
 if (cmd != 0x12345678)
 
 while(1);
 
 while((Spi->SR & SPI_SR_EOT) == 0);
 
 
 if (Spi->SR & SPI_SR_CRCE)
 
 while(1);
 
 SPI_StopReception(SpiId);
 
 
 
 }
 
 
 }
 
 (...)
 
 

My problem is the following.

The communication is working fine hundreds of thousands of times and then fails at Slave side: instead of reading bytes 78 56 34 12 from the SPI FIFO, I read for example 34 12 00 00 or 56 34 12 00.

At first glance one would say it is simply the Slave that is too slow and missed some bytes BUT what is weird is that:

- I get a RXP interrupt which means the Slave has detected correctly the SPI clock during 4 bytes and has sampled the 4 bytes.

- there is no CRC error which means the Slave received the correct bits. For example, when I read 56 34 12 00 from the FIFO the RXCRC was 0x08 which is the CRC of the complete frame 78 56 34 12

It is like there is a problem in the reading of the FIFO.

I've used a logic analyser and didn't identify any electrical issue.

Any idea or suggestion ?

This topic has been closed for replies.

12 replies

waclawek.jan
Super User
June 8, 2020

Read out and post content of SPI registers at slave side.

Have you checked the errata?

JW

Gpeti
GpetiAuthor
Senior
June 8, 2020

Yes I've checked the errata. The only one that applies to my setup is "Master data transfer stall at system clock much faster than SCK". I've implemented the woraround (disable SPI after EOT).

Registers at slave side at slave side, during a faulty reception (more precisely I break in the SPI RXP interrupt handler). In this occurence, I read 34 12 00 00

  • CR1=1 (SPE)
  • CR2= 4 (TSIZE)
  • CFG1: MBR 0x07, CRCEN, UDRCFG=2, FTHVL=3, DSIZE=7
  • CFG2: SSM=1, COMM=2
  • IER=1(RXPIE)
  • SR=0x0001300A ie. CTSIZE=1 (strange but ref manual says "value is not quite reliable when traffic is ongoing on bus"), RXPLVL=1 (??) , EOT=1 (expected), TXP=1 (expected)
  • RXCRC=0x08 (expected for a complete frame). Worth saying the debugger (Keil) does not read the register properly, I read it in my code.

Values of CTSIZE and RXPLVL are not really consistent (at least I don't understand them): since FTHVL=3 (4-data) and TSIZE=4 (same at Master side) , when I get a RXP event, I should have received at least 4 bytes. I don't see how CTSIZE can be 1 , neither why there would be another byte left in the FIFO (RXPLVL=1).

waclawek.jan
Super User
June 8, 2020

I can't see anything wrong here. What's concerning indeed is that while FTHVL=3, it appears that you've read (i.e. had SPI_SR.RXP set) while the FIFO did *not* contain all 4 bytes.

Maybe this is related to the CRC somehow?

However, I don't have experience with 'H7 so I can't help with this more, sorry.

JW

Gpeti
GpetiAuthor
Senior
June 8, 2020

Actually it's the exact same thing without CRC enabled.

Thank you !

waclawek.jan
Super User
June 8, 2020

If you've actually read out only 3 bytes from FIFO, shouldn't the 4th be still there - that would explain RXPLVL and CSIZE, indicating the RXP flag being set prematurely.

At this point, you might want to talk to ST directly, through web support fgorm or through FAE.

JW

Gpeti
GpetiAuthor
Senior
June 10, 2020

Do you confirm that, on slave mode, since I don't need Slave Select, the value of bit SSI does not matter ? (SSM is 1)

berendi
Principal
June 10, 2020

When SSM=1 on the slave, it treats SSI exactly as a chip select/slave select line.

When SSM=1 and SSI=1, CLK and MOSI are ignored, MISO is not driven.

When SSM=1 and SSI=0, MISO is actively driven, bits are shifted in and out on CLK edges.

Gpeti
GpetiAuthor
Senior
June 10, 2020

Thank you, crystal clear.

Gpeti
GpetiAuthor
Senior
June 10, 2020

For the record, I've done many testing, and also checked my code with my colleagues and I'm starting to believe there might be a bug in the SPI peripheral in Slave mode.

I've simplified both slave and master softwares to do only SPI communication in polling. No interrupts or other tasks.

Each exchange consists of:

  • master sends 4 bytes, different every time
  • slave receives (TSIZE=4, Fifo threshold = 4, polling on bit EOT) checks the value and the CRC and send back an answer (different answer if the received word is wrong)
  • master checks answer and start again

Once in a while, as I described, instead of reading for example 78 56 34 12 I read 34 12 00 00 or 12 00 00 00. There is no error status, neither CRCE nor UDR or OVR. The CRC (RXCRC) is always correct.

On 11 millions commands I've got around 100 errors.

I've tried to add a delay after the bit EOT is set. I tried to read byte by byte (LDRB instead of LDR). I've checked the assembly code. It is always the same thing.

I've entered a support request. Waiting for their answer.

Gpeti
GpetiAuthor
Senior
June 10, 2020

The latest version of code (might be useful for STMicro FAE):

Master side:

 SPI1->CFG2 |= SPI_CFG2_COMM_0;
 SPI1->CFG2 &= ~SPI_CFG2_COMM_1;
 
 
 SPI1->CR2 = 4;
 
 
 SPI1->CR1 |= SPI_CR1_SPE;
 SPI1->CR1 |= SPI_CR1_CSTART;
 
 SPI1->TXDR = value++;
 
 
 
 while ( (SPI1->SR & SPI_SR_EOT) == 0 );
 
 if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
 {
 return ( SPI1->SR & SPI_SR_ERR_MASK);
 }
 
 
 SPI1->IFCR = 0xFFFFFFFF;
 
 
 SPI1->CR1 &= ~SPI_CR1_SPE;
 
 Delay(...);
 
 
 SPI1->CFG2 |= SPI_CFG2_COMM_1;
 SPI1->CFG2 &= ~SPI_CFG2_COMM_0;
 
 
 SPI1->CR2 = 4;
 
 
 SPI1->CR1 |= SPI_CR1_SPE;
 SPI1->CR1 |= SPI_CR1_CSTART;
 
 while ( (SPI1->SR & SPI_SR_EOT) == 0 );
 err_reception = 0;
 resp = SPI1->RXDR;
 
 if ( resp == 0x77777777 )
 {
 compt_err ++;
 }
 else
 {
 if ( resp != 0x9ABCDEFF)
 __NOP();
 }
 
 if ( (SPI1->SR & SPI_SR_ERR_MASK) != 0 )
 {
 return ( SPI1->SR & SPI_SR_ERR_MASK);
 }
 
 // clear flags
 SPI1->IFCR = 0xFFFFFFFF;
 
 // disable SPI
 SPI1->CR1 &= ~SPI_CR1_SPE;
 
 
 Delay(...); 
 
 

Slave side:

while(1)
 {
 crc=0;
 
 Spi->CFG2 |= SPI_CFG2_COMM_1;
 Spi->CFG2 &= ~SPI_CFG2_COMM_0;
 Spi->CFG1 = ((Spi->CFG1) & ~SPI_CFG1_FTHLV) | SPI_FIFO_THRESHOLD_04DATA;
 
 Spi->CR2 = 4;
 
 Spi->CFG1 |= SPI_CFG1_CRCEN;
 Spi->UDRDR = 0x66666666;
 
 Spi->CR1 |= SPI_CR1_SPE;
 
 
 while((Spi->SR & SPI_SR_EOT) == 0);
 
 
 cmd = Spi->RXDR;
 
 crc = Spi->RXCRC;
 
 if (cmd != expected_value)
 {
 erreur ++;
 }
 else
 {
 erreur = 0;
 }
 expected_value++;
 
 if (erreur >=2)
 __NOP();
 
 if (Spi->SR & SPI_SR_CRCE)
 while(1);
 
 Spi->IER = 0;
 Spi->IFCR = 0xFFFFFFFF;
 Spi->CR1 &= ~SPI_CR1_SPE; 
 
 Spi->CFG2 |= SPI_CFG2_COMM_0;
 Spi->CFG2 &= ~SPI_CFG2_COMM_1;
 Spi->CR2 = 4;
 Spi->CFG1 |= SPI_CFG1_CRCEN;
 Spi->CFG1 = ((Spi->CFG1) & ~SPI_CFG1_FTHLV) | SPI_FIFO_THRESHOLD_04DATA;
 
 Spi->CR1 |= SPI_CR1_SPE;
 
 if (erreur)
 Spi->TXDR = 0x77777777;
 else
 Spi->TXDR = 0x9ABCDEFF;
 
 while((Spi->SR & SPI_SR_EOT) == 0);
 
 Spi->IER = 0;
 Spi->IFCR = 0xFFFFFFFF;
 Spi->CR1 &= ~SPI_CR1_SPE;
}

Gpeti
GpetiAuthor
Senior
June 11, 2020

Same behaviour in full duplex mode. Same behaviour with slower spi_pclk on slave side.

BTW the ref manual is really not clear regarding the SPI clocks:

"There is no specific constraint concerning the frequency ratio between these clock signals. The user

has to consider a ratio compatible with the data flow speed in order to avoid any data

underrun or overrun events only."

No constraint but you have to consider the good ratio ???

And, later:

"That is why the SPI slave is able to receive and send data even when the spi_pclk and spi_ker_ck clock

signals are inactive.'"

As far as I understood you activate spi_pclk by setting the bit SPI1EN in RCC_APB2_ENR. If you don't do that you can't even write SPI1 registers, so how can you receive anything in slave mode if the spi_pclk is not active ?

berendi
Principal
June 11, 2020

> No constraint but you have to consider the good ratio ???

There is no specific constraint like 1 MHz or 1:128 pclk:ker_ck, but if the FIFO is filled up in the time needed to even read SR and DR (+ software overhead), you lose.

> you activate spi_pclk by setting the bit SPI1EN in RCC_APB2_ENR. If you don't do that you can't even write SPI1 registers

Set RCC_APB2ENR_SPI1EN, write SPI registers, clear RCC_APB2ENR_SPI1EN. It might also mean that RCC_APB2ENR_SPI1EN remains set, but the source of pclk is stopped.

I guess you could then put the MCU in a low-power mode stopping HSE/HSI, and wake up on the next NSS rising edge if the received packet is expected to fit in the FIFO, falling edge if it isn't.