Missing transactions on SPI slave

Hi,

We are using two nRF54l15-PDF to communicate as SPI, one is in master mode and one is in slave mode. The master device sends a simple array of bytes, the first one being incremented at each transaction. The other one simply receive the data using the blocking spi_read_dt function.

The slave receives the data correctly exactly when the master writes, we can see it by having two RTT connected at the same time on each. However, it looks like the slave can either lose a message or get the same one between successives calls to spi_read_dt function.

Examples shown:

master

00> [00:57:45.720,154] <inf> main: sending a message with byte 0xb6
00> [00:57:50.720,554] <inf> main: sending a message with byte 0xb7
00> [00:57:55.720,954] <inf> main: sending a message with byte 0xb8
00> [00:58:00.721,354] <inf> main: sending a message with byte 0xb9
00> [00:58:05.721,754] <inf> main: sending a message with byte 0xba
00> [00:58:10.722,154] <inf> main: sending a message with byte 0xbb
00> [00:58:15.722,554] <inf> main: sending a message with byte 0xbc

slave

[00:04:24.112,272] <inf> main: first bytes received: 0xb6, 0x0
[00:04:24.112,422] <inf> main: client waiting for data...
[00:04:29.112,578] <inf> main: first bytes received: 0xb6, 0x0
[00:04:29.112,729] <inf> main: client waiting for data...
[00:04:34.112,885] <inf> main: first bytes received: 0xb8, 0x0
[00:04:34.113,035] <inf> main: client waiting for data...
[00:04:39.113,191] <inf> main: first bytes received: 0xb8, 0x0
[00:04:39.113,341] <inf> main: client waiting for data...
[00:04:44.113,497] <inf> main: first bytes received: 0xba, 0x0
[00:04:44.113,646] <inf> main: client waiting for data...
[00:04:49.113,804] <inf> main: first bytes received: 0xba, 0x0
[00:04:49.113,954] <inf> main: client waiting for data...
[00:04:54.114,111] <inf> main: first bytes received: 0xbc, 0x0
[00:04:54.114,261] <inf> main: client waiting for data...

This is the master code

#include <stdint.h>
#include <string.h>

#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

#define LEN     130

#define M_NODE  DT_NODELABEL(sub)
#define M_OP    SPI_WORD_SET(8) | SPI_TRANSFER_LSB | SPI_OP_MODE_MASTER

static const struct spi_dt_spec spi = SPI_DT_SPEC_GET(M_NODE, M_OP, 0);

LOG_MODULE_REGISTER(main);

int main(void)
{
    uint8_t rx_data[LEN], tx_data[LEN];
    uint8_t byte = 0;
    int rv;

    struct spi_buf rx_buf = {
        .buf = rx_data,
        .len = sizeof (rx_data)
    };
    struct spi_buf_set rx_set = {
        .buffers = &rx_buf,
        .count = 1
    };

    struct spi_buf tx_buf = {
        .buf = tx_data,
        .len = sizeof (tx_data)
    };
    struct spi_buf_set tx_set = {
        .buffers = &tx_buf,
        .count = 1
    };

    if (!spi_is_ready_dt(&spi)) {
        LOG_ERR("SPI device not ready");
        return 1;
    }

    for (;;) {
        memset(rx_data, 0, sizeof (rx_data));
        memset(tx_data, 0, sizeof (tx_data));

        tx_data[0] = ++byte;

        LOG_INF("sending a message with byte 0x%x", (unsigned)tx_data[0]);
        rv = spi_transceive_dt(&spi, &tx_set, &rx_set);

        if (rv != 0)
            LOG_WRN("SPI error: %s", strerror(rv));

        k_msleep(5000);
    }
}

And this is the slave code

#include <stdint.h>
#include <string.h>

#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

#define LEN     130

#define S_NODE  DT_NODELABEL(main)
#define S_OP    SPI_WORD_SET(8) | SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE

static const struct spi_dt_spec spi = SPI_DT_SPEC_GET(S_NODE, S_OP, 0);

LOG_MODULE_REGISTER(main);

int main(void)
{
    uint8_t rx_data[LEN], tx_data[LEN] = {0xaa, 0xbb};
    int rv;

    struct spi_buf rx_buf = {
        .buf = rx_data,
        .len = sizeof (rx_data)
    };
    struct spi_buf_set rx_set = {
        .buffers = &rx_buf,
        .count = 1
    };

    struct spi_buf tx_buf = {
        .buf = tx_data,
        .len = sizeof (tx_data)
    };
    struct spi_buf_set tx_set = {
        .buffers = &tx_buf,
        .count = 1
    };

    if (!spi_is_ready_dt(&spi)) {
        LOG_ERR("SPI device not ready");
        return 1;
    }

    for (;;) {
        memset(rx_data, 0, sizeof (rx_data));

        LOG_INF("client waiting for data...");
        rv = spi_transceive_dt(&spi, &tx_set, &rx_set);

        if (rv < 0 && rv != -EAGAIN)
            LOG_WRN("SPI error: %s", strerror(rv));
        else
            LOG_INF("first bytes received: 0x%x, 0x%x", (unsigned)rx_data[0], rx_data[1]);
    }
}

Finally, master and slave devicetree overlays respectively:

master

/ {
    aliases {
        spi = &spi;
    };
};

&pinctrl {
    spi_default: spi_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
                    <NRF_PSEL(SPIM_MOSI, 2, 8)>,
                    <NRF_PSEL(SPIM_MISO, 2, 9)>;
            bias-pull-down;
        };
    };

    spi_sleep: spi_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
                    <NRF_PSEL(SPIM_MOSI, 2, 8)>,
                    <NRF_PSEL(SPIM_MISO, 2, 9)>;
            low-power-enable;
        };
    };
};

spi: &spi00 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi_default>;
    pinctrl-1 = <&spi_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio2 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
    sub: sub@0 {
        status = "okay";
        compatible = "vnd,spi-device";
        reg = <0>;
        spi-max-frequency = <10000000>;
        label = "sub";
    };
};

&uart20 {
    status = "disabled";
};

slave

/ {
    aliases {
        spi = &spidev;
    };
};

&pinctrl {
    spidev_default: spidev_default {
        group1 {
            psels = <NRF_PSEL(SPIS_MOSI, 1, 4)>,
                    <NRF_PSEL(SPIS_MISO, 1, 6)>,
                    <NRF_PSEL(SPIS_SCK, 1, 11)>,
                    <NRF_PSEL(SPIS_CSN, 1, 12)>;
        };
    };

    spidev_sleep: spidev_sleep {
        group1 {
            psels = <NRF_PSEL(SPIS_MOSI, 1, 4)>,
                    <NRF_PSEL(SPIS_MISO, 1, 6)>,
                    <NRF_PSEL(SPIS_SCK, 1, 11)>,
                    <NRF_PSEL(SPIS_CSN, 1, 12)>;
            low-power-enable;
        };
    };
};

spidev: &spi20 {
    /delete-property/ rx-delay;
    /delete-property/ rx-delay-supported;

    status = "okay";
    compatible = "nordic,nrf-spis";
    pinctrl-0 = <&spidev_default>;
    pinctrl-1 = <&spidev_sleep>;
    pinctrl-names = "default", "sleep";
    def-char = <0x00>;
    main: main@0 {
        compatible = "vnd,spi-device";
        label = "main";
        reg = <0>;
        spi-max-frequency = <10000000>;
        status = "okay";
    };
};

&uart20 {
    status = "disabled";
};

Are we missing something obvious? Is this behavior caused by the SPI experimental slave support in zephyr? I also have seen that CS GPIO control is not supported by the nRF slave SPI driver, but CSN is used though.

Parents
  • Hi David, 

    I couldn't find any similar issue reported for nRF54L before. I think it's not an issue with the experimental support of the chip. 

    But I don't see a problem with the code you provided. 


    Could you check with a logic analyzer to see what exactly transmitted over the SPI lines ? 
    We need to check if the issue is on the SPIM transmitting or on SPIS receiving.

    Do you have 2 nRF52 DK to verify if it's the issue on NRF54L or can also be reproduced on nRF52 ? 

  • Hi,

    Thank you for your quick answer. We have investigated the issue with also a master written in a separate framework (STM + Cube IDE) and we experienced the same issue. We then inspected through a dedicated SPI oscope plugged into the lines and saw the that the oscope did not interpreted the frames correctly. Even though both the master and the slave did have the exact same configuration the problem stayed.

    We fixed the issue by removing the SPI_MODE_CPOL in both slave zephyr implementation, the master zephyr implementation, and also in the custom other master for STM + Cube.

    We are still trying to understand why removing the option fixes the issue while the configuration was equally set on both side.

Reply
  • Hi,

    Thank you for your quick answer. We have investigated the issue with also a master written in a separate framework (STM + Cube IDE) and we experienced the same issue. We then inspected through a dedicated SPI oscope plugged into the lines and saw the that the oscope did not interpreted the frames correctly. Even though both the master and the slave did have the exact same configuration the problem stayed.

    We fixed the issue by removing the SPI_MODE_CPOL in both slave zephyr implementation, the master zephyr implementation, and also in the custom other master for STM + Cube.

    We are still trying to understand why removing the option fixes the issue while the configuration was equally set on both side.

Children
Related