Hard time to make SPI work in slave mode

Working libraries, libraries being ported and related hardware
castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Dec 07, 2017 8:52 am

Steve thank you for all your help and everyone else as well.

I have SPI DMA Full duplex working as a dream. I'm not using circular buffer and in fact I'm not even using the IRQs from the DMA.
The problem with the "regular" setup is that DMA will trigger an IRQ when the buffer is half/full, however that is not very deterministic since it can trigger for a buffer that is full of noise and get out of sync with Master and corrupting the data.

What I did was attach an interrupt to the SPI CS FALLING/RISING.
During FALLING I re-enable the DMA RX and TX, this will also restart the TX/RX DMA pointer; because the circular buffer flag is not set, DMA is automatically disabled after every full buffer.
During RISING I process the DMA RX buffer as is, not waiting for the RX buffer to be full.

Doing this it allows me to receive an arbitrary RX request size (any size as long is less than the DMA RX Buffer size), Also I can send back an arbitrary TX buffer size.
So far this setup is very flexible since auto-synchronize with every request and RX/TX request size can be any arbitrary size.

However there is a small caveat, since triggering the IRQ and restart the DMA can take some time, the master needs to provide a small idle time during the switch to CS to low or the slave may lose the first byte. If I don't provide a delay I lose the first byte in about 1% of the requests.
Providing an idle time of about 50µs has been proven to be more than enough to capture 100% of the request.

So far I tried to induce noise on the clock and also restart the master or the slave in the middle of requests and everything keeps working as expected.

Soon I'll post my setup for whoever need do something similar in the future.

Again thank you for the help, also because I'm not using double buffer for the DMA buffer I reduced 1028 in the RAM size and everything is under control with a total of about 16300 bytes, which gives me about 4K of RAM to play at runtime.

By the way I discovered why Serial upload binary was 1K less than STLink binary, in board.txt Serial has the option -DSERIAL_USB" :), anyway this would be as last option since not having Serial is pretty bad since all my debugging rely on it, the RAM thread consumption was more about "having" and "knowing" the chip limitations than anything else.
Attachments
DMA Full Duplex.png
DMA Full Duplex.png (156.27 KiB) Viewed 66 times
Last edited by castortiu on Thu Dec 07, 2017 9:07 am, edited 2 times in total.

stevestrong
Posts: 1824
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Thu Dec 07, 2017 9:01 am

I would do the DMA reinitialisation after the data processing, on the rising edge interrupt.
This way at the falling edge (SPI transfer start) you will have the DMA already prepared, so no waiting time is necessary, and no bytes will be lost.

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Dec 07, 2017 9:05 am

True, that is awesome and should work :D

Since at first I was only attaching to FALLING and let the DMA buffer to fill to send me the IRQ since was circular buffer never crossed my mind to change the code to re-init on RISING after changed my code to attach to FALLING/RISING

I'll try now!!

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Dec 07, 2017 9:33 am

It works!!!, reduced my requests from 300µs to 250µs, and in fact because now I'm doing both things (re-init/mark DMA buffer "ready to process") at RISING then I don't need to attach the CS IRQ for CHANGE but just to RISING which allows me to remove all the following code as well

Code: Select all

uint16 ss_bit = PIN_MAP[SLAVE_NSS_PIN].gpio_bit;
volatile uint32* ss_port = portInputRegister(digitalPinToPort(SLAVE_NSS_PIN));

// Read the SS state
bool ss_rising = *ss_port & (1 << ss_bit);

if (ss_rising) {}
else {}
P.S.: I could have cached ss_bit/ss_port however I'm tracking every little bit of RAM I use, now it's even better since is totally gone ....

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Dec 07, 2017 12:58 pm

Now with everything on the DMA setup ready I want to start to work with the data sent by the slave in the master, however I'm having a problem, not a deal breaker but I'm having a problem with the first byte of the MISO, it seems "unreliable".

TxBuffer content is set properly during the response (fixed array of 10 elements pre-allocated during start-up), however is like the first byte sent on MISO is "kind" of the last byte sent from the previous TX response, because I could not explain what was going on, then during DMA reinit I reset the TxBuffer as well "memset(txBuffer, 0, txBufferiSize)"

Then I can see that even if set txBuffer[0] = something, then don't get this byte in the MISO in the current response but in the next one.

So currently what I have done is just ignore the first byte and don't use it in the response, however eventually I would like to know what is going on.

I can't explain it, however is like the DMA controller is not doing what is supposed to do for the first byte.

As you can see in the attachment as a workaraound now I always leave the first byte unset and send the response command ("Time Response") in this case in the following bytes.

Have you ever seen something like this on the SPI MISO?
Attachments
unreliablebyte.png
unreliablebyte.png (36.28 KiB) Viewed 45 times

stevestrong
Posts: 1824
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Thu Dec 07, 2017 3:01 pm

Without seing your code, i can only suggest to
- disable DMA,
- read the SPI data register
- setup and enable the next DMA transfer.

The data to be sent must be valid before you do the last step, because the DMA will deliver the first byte to transmit to SPI when the SPI is enabled (triggered by the TXE flag).

Post Reply