-
We have an application based on a nrf52840 and zephyr 2.7.0. We discovered that the timer counter interrupt latency sometimes is higher than the SPI interrupt interval (100 us), causing an overflow of the DMA buffers. Therefore we enabled void setup(void) {
[..]
IRQ_DIRECT_CONNECT(TIMER2_IRQn, 0, TIMER2_IRQHandler, IRQ_ZERO_LATENCY);
}
ISR_DIRECT_DECLARE(TIMER2_IRQHandler) {
NRF_TIMER2->EVENTS_COMPARE[0] = 0;
/* Toggle buffers */
if(adc_dma_buffer_toggle == 0){ // First Buffer was selected => switch to second buffer
NRF_SPIM3->RXD.PTR = (uint32_t) &adc_dma_buffer1; // Switch to the Second DMA Buffer
}
else { // Second Buffer was selected => switch to first buffer
NRF_SPIM3->RXD.PTR = (uint32_t) &adc_dma_buffer0; // Switch to the First DMA Buffer
// Copy data from inactive buffer to FIFO
[..]
return 1;
} This seems to fix the interrupt latency issue if run standalone. Then we additionally enabled When not using interrupts with Is it possible that BLE and low latency interrupts can not be used at the same time?
|
Beta Was this translation helpful? Give feedback.
Replies: 16 comments 4 replies
-
Under default interrupt configurations (no ZLI), Zephyr Bluetooth Controller uses zephyr interrupt priority level of 0 for Radio ISR, and share its bottom halves with all other SoC peripherals which use level of 1 (refer to dts files for default values). If using ZLI, then Controller Radio ISR and any application set ISR use the same ZLI priority level, and hence Controller will not be able to tolerate ISR latencies of ~100 us introduced by other ISRs. As ZLI cannot use all the Kernel features, please try to not use ZLI if your application can use Zephyr interrupt priority value > 1 for application used SoC peripherals. Zephyr Bluetooth Controller will use priorities 0 and 1, application used SoC peripherals can use 2, 3 etc... |
Beta Was this translation helpful? Give feedback.
-
Dear @cvinayak Thank you for your explanations!
I tested it once more:
Case 1 (No ZLI for the application):
Case 2 (Also ZLI for the application):
In the end, does that mean that, when using BLE (with the given bluetooth config), does not allow to have application interrupt latency < |
Beta Was this translation helpful? Give feedback.
-
Controller does post-processing of PDUs and manages radio event scheduling etc, in a lower priority execution context. This is the bottom half of Radio H/w ISR (which is the top-half).
By
Zephyr Bluetooth Controller's bottom half ISRs consume > 100 us, but the Radio ISR (priority value 0) consume < 100us. You could lower this |
Beta Was this translation helpful? Give feedback.
-
Dear @cvinayak Thank you once more for your support! I think I understood it now enough. I still get occationally a I tried to measure its time consumption with following code:
I got timmings of mostly Followup question:
I know I could do this by giving a semaphore to a thread, but I am a bit worried that the thread switching overhead is too large. |
Beta Was this translation helpful? Give feedback.
-
Are you sure you have set the zephyr priority value for your timer to 1 and not using |
Beta Was this translation helpful? Give feedback.
-
Dear @cvinayak You were right, I still had my timer flagged with We have an ADC which fires its DRDY (Dtata Ready) line every 100us. Most of the time this works correctly, how ever, sometimes we saw that SPIM3 transfered 105 samples before we could swap the buffers! static const nrfx_spim_t spi3 = NRFX_SPIM_INSTANCE(3);
static nrf_ppi_channel_t ppi_Adc = NRF_PPI_CHANNEL31;
static nrf_ppi_channel_t ppiChannel_AdcTimerCounter = NRF_PPI_CHANNEL31;
static const nrfx_timer_t conv_counter = NRFX_TIMER_INSTANCE(2);
uint8_t adcDmaBuffer[2][30][ADC_SAMPLE_SIZE]; // ADC DMA Buffers (Dualbuffer with 2 slots)
uint8_t adcDmaSlotIndex = 0;
void init(void) {
/* Timer Counter init */
NRF_TIMER2->MODE = TIMER_MODE_MODE_Counter; // Set the timer in Counter Mode
NRF_TIMER2->TASKS_CLEAR = 1; // clear the task first to be usable for later
NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; // Set counter to 16 bit resolution
NRF_TIMER2->CC[0] = ADC_DMA_TRANSFER_SIZE_SAMPLES; // Set value for TIMER2 compare register 0 (Number of samples to fit in the DMA buffer)
// Enable interrupt on Timer 2 compare match events
NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
// Enable shortcut on Compare Event
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
IRQ_DIRECT_CONNECT(TIMER2_IRQn, TIMER_IRQ_PRIORITY, TIMER2_IRQHandler, 0);
nrfx_ppi_channel_alloc(&ppiChannel_AdcTimerCounter); // Connect ADC_DRDY falling edge event to counter input event
nrfx_ppi_channel_assign(ppiChannel_AdcTimerCounter, nrfx_gpiote_in_event_addr_get(ADC_nDRDY), nrfx_timer_task_address_get(&conv_counter, NRF_TIMER_TASK_COUNT));
NVIC_EnableIRQ(TIMER2_IRQn); // Enable the interrupt
/* ADC Init */
NRF_SPIM3->RXD.MAXCNT = ADC_SAMPLE_SIZE;
NRF_SPIM3->RXD.PTR = (uint32_t) &adcDmaBuffer[adcDmaSlotIndex];
NRF_SPIM3->RXD.LIST = 1;
nrfx_gpiote_in_event_enable(ADC_nDRDY, true);
nrfx_ppi_channel_alloc(&ppi_Adc);
nrfx_ppi_channel_assign(ppi_Adc, nrfx_gpiote_in_event_addr_get(ADC_nDRDY), nrfx_spim_start_task_get(&spi3);
nrfx_ppi_channel_enable(ppi_Adc);
}
/* Timer Handler */
ISR_DIRECT_DECLARE(TIMER2_IRQHandler) {
uint32_t dmaWritePtr;
NRF_TIMER2->EVENTS_COMPARE[0] = 0; // Clear compare register 0 event
dmaWritePtr = NRF_SPIM3->RXD.PTR; // Backup for later checks (how many samples did effectively got written until we got called)
if(adcDmaSlotIndex == ADC_DMA_SLOT_0){
NRF_SPIM3->RXD.PTR = (uint32_t) &adcDmaBuffer[ADC_DMA_SLOT_1];
adcDmaSlotIndex = ADC_DMA_SLOT_1;
}
else {
NRF_SPIM3->RXD.PTR = (uint32_t) &adcDmaBuffer[ADC_DMA_SLOT_0];
adcDmaSlotIndex = ADC_DMA_SLOT_0;
}
// END_CTRITICAL_SECTION
/* From now on we have time to do what ever we need. */
/* Check how many samples effectively got copied by SPIM3 */
adcDmaSlotIndex_t adcDmaSlotPreviousIndex;
/* The slots got already swapped, but we need to access the now inactive slot */
if(adcDmaSlotIndex == ADC_DMA_SLOT_0){
adcDmaSlotPreviousIndex = ADC_DMA_SLOT_1;
}
else {
adcDmaSlotPreviousIndex = ADC_DMA_SLOT_0;
}
dmaBytesTranfered = dmaWritePtr - (uint32_t)&adcDmaBuffer[adcDmaSlotPreviousIndex]; // subtract buffer start by buffer start address => transfered bytes
samplesInDmaBuffer = dmaBytesTranfered / ADC_SAMPLE_SIZE;
__ASSERT(samplesInDmaBuffer > 30, "We were too slow, the SPIM3 transfered %d samples instead of only 30! => RAM corruption!!!", samplesInDmaBuffer);
// Most of the time we see 30, rarely 31, but sometimes even 105!
[..]
return 1;
} |
Beta Was this translation helpful? Give feedback.
-
Update question: |
Beta Was this translation helpful? Give feedback.
-
Maybe @anangl can answer that |
Beta Was this translation helpful? Give feedback.
-
What value are you using for |
Beta Was this translation helpful? Give feedback.
-
I used priority 1. Would it work to change my timer interrupt priority also to 0? |
Beta Was this translation helpful? Give feedback.
-
Ok, continue to keep Did you have these? And please use lower priorities for other SoC peripherals used in your application by having the below
inside your application folder in Maybe 10.5 ms latency seen could be from other SoC Peripheral ISRs which are by default at value 1. |
Beta Was this translation helpful? Give feedback.
-
ok
2x yes
Is there a way to check if this got correctly applied? I will have to let it run for a few hours to check if it helps. Something else I discovered, maybe not directly related to the IRQ priorities:
I think this might happen when How ever, sporadically I see that the
This doesn't make any sense to me, especially because before and after this call it is correctly 30.
|
Beta Was this translation helpful? Give feedback.
-
You can check the |
Beta Was this translation helpful? Give feedback.
-
@caco3 converted this into a discussion, you can keep discussing there and you can still mention Vinayak with @cvinayak. |
Beta Was this translation helpful? Give feedback.
-
Thank you all for your valuable input. After I set the priorities according your suggestions, the system runs now very stable. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
Ok, continue to keep
TIMER_IRQ_PRIORITY=1
.Did you have these?
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
andCONFIG_BT_CTLR_ULL_HIGH_PRIO=2
And please use lower priorities for other SoC peripherals used in your application by having the below
#define NRF_DEFAULT_IRQ_PRIORITY 5
inside your application folder in
dts/arm/nordic/override.dtsi
file.Maybe 10.5 ms latency seen could be from other SoC Peripheral ISRs which are by default at value 1.