spi_done event is delayed for unknown reason

hello Nordic

i am working with nrf52832 with ncs v1.9.2 with zephyr

i am using spim to read from a sensor

the spi xfer command is done via ppi with a timer ~20kHz

there is another timer set raise a gpiote, also via ppi, that tells the sensor start measure, also at the same speed.

the spi_done callback calles on a handler that pull the sensor gpiote back down, before it is raised back for the next conversion, and copy the data from the spi buffer.

the problem i see with a picoscop is that the spi_done callback takes to much time to happen (more then 46microsec after an 8 bytes data read spi_xfer is called (spi_tx is 0 bytes) ) . this does not happen all the time (and not on all of the boards). this really mess things up as the handler may be called while next spi_xfer is in process and by that trying to copy the buffers that are being used by the spi, off course we also loos a sample because the sensor gpiote was not pulled down and back up because the ppi task to raise the gpiote did nothing for the already raised gpiote. 

this is a photo from the picoscop catch:

line color is brown for enter and exit the spi_done event handler

blue line is the spi clk and the

red line is the sensore start measure indication gpiote 

this is the event handler code:

void ads8866_spi_done_event_handler(void *ctx, uint8_t *rx_buff, int size)
{
    const struct device                   *ads8866_chain = (const struct device *)ctx;
    const struct ads8866_chain_dev_config *cfg = ads8866_chain->config;
    struct ads8866_chain_data             *drv_data = ads8866_chain->data;
    static int                             run_idx = 0;

    gpio_pin_set(gpio_dev_red, 25, 1);

    nrfx_gpiote_clr_task_trigger(cfg->conv_gpio.pin);

#ifdef CONFIG_ADS8866_CHAIN_ACC_CHANNELS_CONNECTED
    vib0_arrays[fill_sample_buff_idx][run_idx] = 0x8000 ^ (uint16_t)(((uint16_t)(rx_buff[0]) << 8) | rx_buff[1]);
    vib1_arrays[fill_sample_buff_idx][run_idx] = 0x8000 ^ (uint16_t)(((uint16_t)(rx_buff[2]) << 8) | rx_buff[3]);
    vib2_arrays[fill_sample_buff_idx][run_idx] = 0x8000 ^ (uint16_t)(((uint16_t)(rx_buff[4]) << 8) | rx_buff[5]);
    mag_arrays[fill_sample_buff_idx][run_idx] = 0x8000 ^ (uint16_t)(((uint16_t)(rx_buff[6]) << 8) | rx_buff[7]);
#else
    mag_arrays[fill_sample_buff_idx][run_idx] = 0x8000 ^ (uint16_t)(((uint16_t)rx_buff[0] << 8) | rx_buff[1]);
#endif

    run_idx++;
    if (SAMPLE_BUFF_SIZE <= run_idx)
    {
        run_idx = 0;
        if (true == app_buff_busy)
        {
            buff_overrun = true;
            return;
        }
        int temp_idx = app_used_buff_idx;
        app_used_buff_idx = fill_sample_buff_idx;
        fill_sample_buff_idx = temp_idx;
        app_buff_busy = true;
        k_sem_give(&drv_data->thread_sem);
    }
    gpio_pin_set(gpio_dev_red, 25, 0);
}

this is the dts part of the sensor with the spi 

&spi1 {
	status = "okay";
	sck-pin = <8>;    // gpio 0 pin 8
	mosi-pin = <0xFF>;
	miso-pin = <7>;    // gpio 0 pin 7
	compatible = "nordic,nrf-spim";
	ads8866_chain@0 {
		compatible = "ti,ads8866_chain";
		reg = <0>;
		chain_length = <4>;
		def_sample_freq = <20833>;
		conv_gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
		str_gpios = <&gpio0 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>;
		stbr_gpios = <&gpio0 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>;
		label = "ADS8866_CHAIN";
		pm_mag_en_gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
		pm_5v_en_gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
		spi-max-frequency = <8000000>;
	};
};

&spi2 {
	status = "okay";
	sck-pin = <5>; // gpio 0 pin 5
	mosi-pin = <4>; // gpio 0 pin 4
	miso-pin = <2>; // gpio 0 pin 2
	compatible = "nordic,nrf-spim";
	cs-gpios = <&gpio0 3 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
	augu_flash_is25lp128: augu_flash_is25lp128@0 {
		compatible = "issi,augu_flash_is25lp128";
		reg = <0>;
		label = "AUGU_FLASH_IS25LP128";
		jedec_id = [ 9d 60 18  ];
		spi-max-frequency = <8000000>; //<133000000>;
		page_size = <256>;
		block_size = <65536>;
		size = <0x8000000>;
	};
};

any idea what can cause this delay ?

how can i check what is causing this delay, as it is supposed to be high priority irq  ?

(we don't know of anything else running at the same time but every 32 samples taken we write the data to external flash with another spi instance. the problem does not occur in corresponds to the time of every flash writing ?

hope to read from you soon 

best regards

Ziv

  • BTLE/radio processing can take a significant amount of time where no other (lower priority) interrupts are allowed to run.

  • I've answered the related question:  use spi dma/easyDMA in a fifo manure, or cyclic queue . 

    As for DMA latencies, it depends on whether another DMA master is also trying to access the same RAM block as your SPIM peripheral. If you need to guarantee DMA access latency you will need to dedicate an entire RAM block to a given DMA master (f.ex. dedicate the RAM6 AHB slave to SPIM0 AHB master), see Memory spec. 

  • yes i have 2 spi masters, one for flash streaming the data (which is not called via ppi) and one for reading the samples from sensor (which is called via ppi). is it possible to define different priorities for each spi (for example spi0 and spi2) in the way they use DMA buffer ?  

    If you need to guarantee DMA access latency you will need to dedicate an entire RAM block to a given DMA master

    one block you mean give it 4k bytes of RAM ?  i can not take so much ram on a 64k RAM soc only for dma of 256bytes max page write, or 8 bytes of samples. can't i define some smaller DMA buffers for each SPI ?

    or maybe it is just a division of the RAM but the relevant section is still free for use.. in that case how do i decide the RAM to 2 sections (one for each dma salve of spi)?

    and in any case if DMA latency is my problem, the suggested approach of using DMA as fifo :

    I've answered the related question:  use spi dma/easyDMA in a fifo manure, or cyclic queue . 

    will not solve the problem of latency but maybe just hide it. am i right ?

  • ziv123 said:
    one block you mean give it 4k bytes of RAM ?  i can not take so much ram on a 64k RAM soc only for dma of 256bytes max page write, or 8 bytes of samples.

    Yes. As one block of ram is controlled by one AHB slave, and one AHB slave can only service one AHB master at a time. If one AHB slave is buisy serving an AHB master's R/W access to its RAM block, all other AHB masters requesting R/W access to the same RAM block will have to wait until the AHB slave has finished the current AHB masters RAM access (ie. until a DMA trasfer is complete, or the CPU has finished a R/W to this RAM block). 

    ziv123 said:
    or maybe it is just a division of the RAM but the relevant section is still free for use.. in that case how do i decide the RAM to 2 sections (one for each dma salve of spi)?

    Won't work, as both/all sections of a single RAM block are controlled by the same AHB slave. 

    If you can't allocate/dedicate entire RAM blocks to single AHB masters, you can spread out the access load across your RAM blocks. 

    If you know that one SPIM needs large/long accesses to RAM, like a data dump to ext flash, then you can put this SPIM's buffers in a RAM block that is not also used by the other SPIM that needs short and latency-constrained accesses to RAM. 

    The same is true for the CPU, put data with low RAM access loads in the same RAM blocks as the one's you've used for the two SPIM peripherals. That way you can still use the full 4KB RAM block, but you've lowered the propability that your SPIMs DMA transfers get blocked for a significant amount of time. 

    Note that this is a core feature of the AHB bus architechture. 

    ziv123 said:
    will not solve the problem of latency but maybe just hide it. am i right ?

    Solve and hide depends entirely on your latency/timing requirements. What exactly are your latency/timing requirements?  

Related