Skip to content

SPI transfers sometimes corrupt last received byte #2200

Closed
@webmeister

Description

@webmeister

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:

  1. The problem happens only for data that is received, not for data that is sent.
  2. The data on the line looks fine, my logic analyzer shows the correct data.
  3. 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.
  4. 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.
  5. 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.
  6. Raspberry Pi 2 B is affected as well.
  7. (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>;
			};
		};
	};
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    Waiting for external inputWaiting for a comment from the originator of the issue, or a collaborator.Waiting for internal commentWaiting for comment from a member of the Raspberry Pi engineering team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions