Closed
Description
I use several Raspberry Pis to communicate with other devices via SPI. There, I observed a strange data corruption that I was able to reproduce with a Raspberry Pi 3 B alone, i.e. with SPI in loopback mode (MOSI connected to MISO). I did several experiments to narrow down the problem:
- The problem happens only for data that is received, not for data that is sent.
- The data on the line looks fine, my logic analyzer shows the correct data.
- It is always the last byte of the transfer that is affected. It seems that this byte is not received at all but instead taken from the previous transfer.
- Only certain transfer sizes seem to be affected (at 5 MHz). I tested 1 to 150 byte transfers and saw the problem only for 56 to 72 byte transfers and for 111 to 136 byte transfers. Notice how these are roughly the same ranges, just twice the size. For all other sizes 500k transfers of random data did not show the problem.
- Only certain clock frequencies seem to be affected (for 64 byte transfers). I tested 1 to 62.5 MHz (in steps of 500 kHz; with 63 MHz and beyond no reliable communication seems possible) and saw the problem only for 4 to 9.5 MHz.
- Raspberry Pi 2 B is affected as well.
- (Almost) current mainline kernel (4.13-rc2) is affected, as well as some older version (4.11-rc4).
For my experiments on the Raspberry Pi 3 (with kernel 4.13-rc2) I used the following Python script:
import spidev
spi = spidev.SpiDev()
spi.open(32766, 1)
spi.max_speed_hz = 5000000
size = 64
for iteration in range(500000):
data_send = size * [iteration % 256]
data_recv = spi.xfer(data_send)
if data_send != data_recv:
print(iteration)
print(data_send)
print(data_recv)
break
When running this script, you should see that as soon as it detects a mismatch, the wrong byte belongs to the previous transfer (for example, when it transferred 64 times the value 111, the last byte received will still have the value 110 from the previous transfer).
And for reference, this is the device tree overlay used to activate spidev:
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/soc/gpio@7e200000";
__overlay__ {
spi0_pins: spi0_pins {
brcm,pins = <9 10 11>;
brcm,function = <4>;
};
spi0_cs_pins: spi0_cs_pins {
brcm,pins = <8 7>;
brcm,function = <1>;
};
};
};
fragment@1 {
target-path = "/soc/spi@7e204000";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
status = "okay";
spidev1: spi@1{
compatible = "spidev";
reg = <1>;
spi-max-frequency = <5000000>;
};
};
};
};