Hard fault in spi_nrfx_spim.c>configure when writing to a SPIM interface without a CLK defined

Test platform: nRF54L15-DK
NRF Connect 2.9.0
When calling spi_write_dt, under the hood spi_nrfx_spim.c > configure is called to prepare the SPIM interface for a transmission.
The following line in spi_nrfx_spim.c > configure produces a hard fault:
	nrfy_gpio_pin_write(nrfy_spim_sck_pin_get(dev_config->spim.p_reg),
			    spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0);
nrfy_spim_sck_pin_get queries the harware SPIM device for the CLK pin.
If no CLK pin is configured, UINT_MAX is returned.
nrfy_gpio_pin_write sets the pin to the correct level for the pending transmission.
However, if no CLK pin is configured, nrfy_pio_pin_write will try to set pin 4294967295, which results in a fault.
Solution: Check if the CLK pin is valid before setting it.
    uint32_t sck_pin = nrfy_spim_sck_pin_get(dev_config->spim.p_reg);
	if (sck_pin != UINT_MAX) {
		nrfy_gpio_pin_write(sck_pin,
				    spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0);
	}
Background:
The WS2812 LED driver uses the SPI interface for a 1 wire serial interface.
To use this 1-wire interface, only MOSI is required, the CLK pin can be left unconnected.
Parents
  • As an aside, apart from the bug here, my findings were that if no physical pin was defined for the SPIM CLK pin the hardware (ie not the driver) SPIM simply didn't work at all, as it appears to use the CLK pin as the SPIM clock, not the internal 16MHz source for CLK. This is not unreasonable, given that SPIS mode uses the SCK pin. So even if the CLK pin is unused for SPIM, it must be still defined and active. Any updates on this would be appreciated.

Reply
  • As an aside, apart from the bug here, my findings were that if no physical pin was defined for the SPIM CLK pin the hardware (ie not the driver) SPIM simply didn't work at all, as it appears to use the CLK pin as the SPIM clock, not the internal 16MHz source for CLK. This is not unreasonable, given that SPIS mode uses the SCK pin. So even if the CLK pin is unused for SPIM, it must be still defined and active. Any updates on this would be appreciated.

Children
  • Hi Hugh, 
    SPIM peripheral was not made to be used as 1-wire interface. Standard SPIM requires a CLK. So a CLK pin has to be assigned. I don't see a problem to assign it to an unused pin. You will need to follow the Pin Assignment guide at chapter 10 in the spec. 

  • Hi Hung and Hugh,

    I just did another test with the nRF54L15 SPIM device without a CLK pin configured, and the patch in spi_nrfx_spim.c applied. The MOSI output outputs the signal as expected.

    Here is the devicetree (note that no CLK pin is configured)

    &spi21 {
        compatible = "nordic,nrf-spim";
    	  status = "okay";
    	  pinctrl-0 = <&spi21_default>;
    	  pinctrl-1 = <&spi21_sleep>;
    	  pinctrl-names = "default", "sleep";
    	  gendev: gendev@0 {
    		  compatible = "vnd,spi-device";
    		  reg = <0>;
    		  spi-max-frequency = <1000000>;
    		  label = "testdev";
    	};
    };
    
    &pinctrl {
    	spi21_default: spi21_default {
    		group1 {
    				psels = <NRF_PSEL(SPIM_MOSI, 1, 13)>;
    		};
    	};
    
    	spi21_sleep: spi21_sleep {
    		group1 {
    				psels = <NRF_PSEL(SPIM_MOSI, 1, 13)>;
    				low-power-enable;
    		};
    	};
    };

    Here is the main.c:

    #include <stdio.h>
    #include <zephyr/kernel.h>
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #define SLEEP_TIME_MS   100
    
    #define SPIOP      (SPI_WORD_SET(8) | SPI_TRANSFER_MSB)
    struct spi_dt_spec spispec = SPI_DT_SPEC_GET(DT_NODELABEL(gendev), SPIOP, 0);
    
    int main(void)
    {
    	if (!spi_is_ready_dt(&spispec)) {
    		return 1;
    	}
    
    	uint8_t tx_buf[1] = {0xAA};
    	struct spi_buf tx_bufs[] = {{ .buf = tx_buf, .len = sizeof(tx_buf),}};
    	struct spi_buf_set tx = {.buffers = tx_bufs, .count = 1};
    
    
    	while (1) {
    		int err = spi_write_dt(&spispec, &tx);
    		if (err) {
    			return 2;
    		}
    
    		k_msleep(SLEEP_TIME_MS);
    	}
    	return 0;
    }

    Here is the output on MOSI (pin 1.13):

    MOSI outputs 0xAA

    Cheers,

    Georg

  • Good to know, thanks for the update. That is different behaviour to the nRF52 series.

  • I just realized that in register PSEL.SCN bit 31 indicates if the CLK pin is disconnected.

    From the NRF54L15 datasheet:

    so the patch should actually be slightly modified:

    	uint32_t sck_pin = nrfy_spim_sck_pin_get(dev_config->spim.p_reg);
    	bool sck_pin_disconnected = sck_pin & 0x80000000;
    	if (!sck_pin_disconnected) {
    		nrfy_gpio_pin_write(sck_pin,
    				    spi_cfg->operation & SPI_MODE_CPOL ? 1 : 0);
    	}

Related