Page 1 of 2

Need help on High-Level Interrupts

Posted: Wed May 25, 2022 4:44 pm
by yuwenpeng
I'm trying to implement a high level interrupt to reduce the interrupt latency and jitter. The interrupt source is a GPIO that connects to pulse-per-second signal from a GPS module. I write the interrupt handler in assemble and register the interrupt in app_main with priority level 5. The code is attached below.
The problem is when I try to register the interrupt with level 5, it crashed at line

Code: Select all

gpio_isr_handler_add((gpio_num_t)PIN_PPS, xt_highint5, (void*) PIN_PPS);
, and the error message is

Code: Select all

Core  0 panic'ed (LoadProhibited)
If I register the interrupt as level 3, the registration is successful but then it stuck somewhere in/after the interrupt handler. I've tried all the method I can think of, such as restoring the register values, loading EXCSAVE_3 or EXCSAVE_5 to A0, but none of them works. Could anyone give me some hint what should I do to fix it? Or any document/post I can read?

Exception message when register the interrupt with level 5 priority.

Code: Select all

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400e4e0f  PS      : 0x00060f33  A0      : 0x800d59ce  A1      : 0x3ffb60d0  
0x400e4e0f: esp_intr_get_cpu at C:/Espressif/esp/esp-idf/components/esp_hw_support/intr_alloc.c:704

A2      : 0x00000000  A3      : 0x3ffb2558  A4      : 0x00000020  A5      : 0x3ffb12a4
A6      : 0x3ffb722c  A7      : 0x0000000c  A8      : 0x800d5824  A9      : 0x3ffb6090
A10     : 0x3ff44000  A11     : 0x0000001b  A12     : 0x00000001  A13     : 0x00060f23
A14     : 0x00000000  A15     : 0x00000000  SAR     : 0x00000005  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000000  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xfffffffa  

Backtrace:0x400e4e0c:0x3ffb60d00x400d59cb:0x3ffb60f0 0x400d4a0f:0x3ffb6120 0x400e54fc:0x3ffb6140 0x40087a09:0x3ffb6160
0x400e4e0c: esp_intr_get_cpu at C:/Espressif/esp/esp-idf/components/esp_hw_support/intr_alloc.c:703
0x400d59cb: gpio_isr_handler_add at C:/Espressif/esp/esp-idf/components/driver/gpio.c:473
0x400d4a0f: app_main at C:\Espressif\esp\UGA_ESP32_C\build/../main/main.c:27
0x400e54fc: main_task at C:/Espressif/esp/esp-idf/components/freertos/port/port_common.c:129 (discriminator 2)
0x40087a09: vPortTaskWrapper at C:/Espressif/esp/esp-idf/components/freertos/port/xtensa/port.c:131
Code of "Main.c"

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "xtensa/core-macros.h"
#include "driver/timer.h"
#include "driver/gpio.h"
#include <driver/timer.h>

#define PIN_PPS 27

int32_t PPSCount_ASM[2];

void xt_highint5(void * arg);

void app_main(void)
{
    PPSCount_ASM[0] = 0;
    gpio_pad_select_gpio(PIN_PPS);
    gpio_set_direction((gpio_num_t)PIN_PPS, GPIO_MODE_INPUT);
    gpio_pullup_dis((gpio_num_t)PIN_PPS);
    gpio_pulldown_dis((gpio_num_t)PIN_PPS);
    gpio_set_intr_type((gpio_num_t)PIN_PPS, GPIO_INTR_POSEDGE); //rising edge

    printf("Initial before PPS\r\n");
    gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5);
    gpio_isr_handler_add((gpio_num_t)PIN_PPS, xt_highint5, (void*) PIN_PPS);
    gpio_intr_enable((gpio_num_t)PIN_PPS);
    printf("Initial after PPS\r\n");
    while(1)
    {
        printf("\n%d",PPSCount_ASM[0]);
        vTaskDelay(300/portTICK_PERIOD_MS);
    }
    return;
}
Code of interrupt handle

Code: Select all

#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include "soc/gpio_reg.h"
#define DRDY_INT_PIN_BITMASK    (1<<27)  // GPIO27 as interrupt pin

.data
_l5_intr_stack:
.space      8

    .section .iram1,"ax"
    .global     xt_highint5
    .type       xt_highint5,@function
    .align      4
xt_highint5:
    //save register values
    movi    a0, _l5_intr_stack
    s32i    a13,    a0,     0
    s32i    a15,    a0,     4

    // cleare the interrupt status of GPIO27(used interrupt pint!)
    movi a13, GPIO_STATUS_W1TC_REG
    movi a15,  DRDY_INT_PIN_BITMASK
    s32i a15, a13, 0

    movi    a13, PPSCount_ASM
    l32i    a15, a13, 0
    //memw
    addi.n  a15, a15, 4
    s32i    a15, a13, 0

    //restore register values
    movi    a0, _l5_intr_stack
    l32i    a13,    a0,     0
    l32i    a15,    a0,     4

    rsr     a0,     EXCSAVE_3
    rfi     3

    .global ld_include_highint_hdl_my
ld_include_highint_hdl_my:

Re: Need help on High-Level Interrupts

Posted: Thu May 26, 2022 2:56 am
by ESP_Sprite
That doesn't work because (simplifying a bit) the GPIO block only has one interrupt handler; the GPIO driver hooks into that using its own interrupt handler and that then figures out what GPIO caused the interrupt and calls the corresponding handler. As the driver is written in C, you can't assign it to interrupt level 5. The way to do that is not to use the driver and manually do what is needed using intr_alloc() and poking the hardware to set up an interrupt on the pin you need.

Re: Need help on High-Level Interrupts

Posted: Thu May 26, 2022 4:53 am
by yuwenpeng
Thank you very much for helping out!!
When my post is under reviewing, I realized gpio_isr_handler_add(...); is not the correct function to register high-level interrupt, I should use esp_intr_alloc() instead. With esp_intr_alloc() it seems the high-level interrupt registration is done successfully, but I still get problems.
To my understanding, only 1 handler slot is provided for each interrupt level. Taking level 5 for example, the interrupt vector _Level5Vector is defined in xtensa_vectors.S file, in which it call0 xt_highint5 to start interrupt handling. There is a empty weak xt_highint5 defined in xtensa_vector_default.S file. But if user defined xt_highint5 somewhere else, it will overwrite the default one.
My problems is that, after I registered level 5 interrupt, the pulse signal did trigger the interrupt, but the interrupt handling seems stuck in _Level5Vector or the default xt_highint5, and finally triggered watch dog. I can't figure out why the user-defined xt_highint5 is not called and why it stuck in the default one.
Could you please help me to take a look at the code? I've struggled with it for 5 hours, but still can't get it to work... I borrowed some of the code from the postviewtopic.php?f=13&t=15213&start=10. That code worked for him, but I'm not the lucky one....

The minimal code is attached below.
Main.c code

Code: Select all

#define PIN_PPS 27
int32_t PPSCount_ASM[2];
void RegisterInt(void *you_need_this)
{
    gpio_config_t interrupt_pin={
        .intr_type=GPIO_INTR_POSEDGE,
        .pin_bit_mask=(1ULL<<PIN_PPS),
        .mode=GPIO_MODE_INPUT,
        .pull_up_en=GPIO_PULLUP_DISABLE,
        .pull_down_en=GPIO_PULLDOWN_DISABLE,
    };
    ESP_ERROR_CHECK(gpio_config(&interrupt_pin));
    esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_IRAM, NULL, NULL, NULL);
    while(1) vTaskDelay(300/portTICK_PERIOD_MS);
}
void app_main(void)
{
    PPSCount_ASM[0] = 0;
    xTaskCreatePinnedToCore(RegisterInt,"allocEXTGPIOINT",4096,NULL,0,NULL,0);
    while(1){
        printf("\n%d",PPSCount_ASM[0]);
        vTaskDelay(300/portTICK_PERIOD_MS);
    }
}
The following is the interrupt handler in assemble language.

Code: Select all

#define DRDY_INT_PIN_BITMASK    (1<<27)  // GPIO27 as interrupt pin

.data
_l5_intr_stack:
.space      8
    .section .iram1,"ax" 
    .global     xt_highint5   
    .type       xt_highint5,@function
    .align      4
xt_highint5:
    //save register values
    movi    a0,     _l5_intr_stack
    s32i    a13,    a0,     0
    s32i    a15,    a0,     4

    // cleare the interrupt status of GPIO27(used interrupt pint!)
    movi a13, GPIO_STATUS_W1TC_REG
    movi a15,  DRDY_INT_PIN_BITMASK
    s32i a15, a13, 0
    //update pps counter
    movi    a13, PPSCount_ASM
    l32i    a15, a13, 0
    addi.n  a15, a15, 8
    s32i    a15, a13, 0
    //restore register values
    movi    a0, _l5_intr_stack
    l32i    a13,    a0,     0
    l32i    a15,    a0,     4
    //return
    rsr     a0, EXCSAVE_5                   /* restore a0 */
    rfi     5
//define new function to make sure it is linked.
    .global ld_include_highint_hdl_my
ld_include_highint_hdl_my:
watch dog error message:

Code: Select all

W (186) boot.esp32: PRO CPU has been reset by WDT.
W (192) boot.esp32: WDT reset info: PRO CPU PC=0x400840c0
0x400840c0: _xt_highint5 at C:/Espressif/esp/esp-idf/components/freertos/port/xtensa/xtensa_vector_defaults.S:194
W (198) boot.esp32: WDT reset info: APP CPU PC=0x400e5316 (waiti mode)
0x400e5316: esp_pm_impl_waiti at C:/Espressif/esp/esp-idf/components/esp_pm/pm_impl.c:839
or

Code: Select all

W (186) boot.esp32: PRO CPU has been reset by WDT.
W (192) boot.esp32: WDT reset info: PRO CPU PC=0x40080240
0x40080240: _Level5Vector at C:/Espressif/esp/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1640
W (198) boot.esp32: WDT reset info: APP CPU PC=0x400e5316 (waiti mode)
0x400e5316: esp_pm_impl_waiti at C:/Espressif/esp/esp-idf/components/esp_pm/pm_impl.c:839

Re: Need help on High-Level Interrupts

Posted: Thu May 26, 2022 6:40 am
by ESP_Sprite
Just to make sure, you did the '-u ld_include_highint_hdl_my' trick in CMakeLists.txt to make sure your weak function actually gets linked in?

Re: Need help on High-Level Interrupts

Posted: Thu May 26, 2022 7:11 pm
by yuwenpeng
Yes, that's the reason! I added the followed line into CMakeLists.txt and then it works. Appreciate you pointing it out!

Code: Select all

target_link_libraries(${COMPONENT_TARGET} "-u ld_include_highint_hdl_my")
After doing more test, I found that by calling intr_matrix_set( , , ) directly, I can register the interrupt on either core 0 or core 1. But with function esp_intr_alloc(...), I can only register the interrupt on core 0. When I pin esp_intr_alloc(...) to core 1, the interrupt is never triggered. Also, when I register level4 or 6 interrupt with esp_intr_alloc(...), the interrupt is never triggered too. Did I missed something? Or could there be something wrong in esp_intr_alloc(...)?

I have another question, which register should I set to clear the interrupt flag of timer group 0 timer 0?
Using intr_matrix_set(..), I connected level 5 interrupt 31 with timer interrupt source (ETS_TG0_T0_LEVEL_INTR_SOURCE), and it seems xt_highint5() was executed. So I need to clear the interrupt flag for the next timer alarm.

Best regards,
Wayne

Re: Need help on High-Level Interrupts

Posted: Fri May 27, 2022 12:56 am
by ESP_Sprite
For the intr_alloc thing: I seem to recall there's something odd with the GPIO interrupts in that they're per core.. you may also need to do the rest of the setup on core 1 for it to work. Level 4 interrupts are used for the panic handler, and I don't think level 6 interrupts are normal interrupts. For the timer interrupt clear bit, see the trm, it should be documented there.

Re: Need help on High-Level Interrupts

Posted: Fri May 27, 2022 3:48 am
by yuwenpeng
Hi ESP_Sprite,
Thanks a lot! I'll dig more to find the difference between the 2 register methods, and you are right, only interrupt XCHAL_DEBUGLEVEL is at level 6 according to core-isa.h.
Thank you for the technical reference, it will be very helpful.
Regarding clearing the timer interrupt flag, I got the address by &(TIMERG0.int_clr_timers.val), similarly read timer counter from &(TIMERG0.hw_timer[TimerNo].cnt_low) or got the address of other needed variables.
Now I can connect both timer interrupt and GPIO interrupt to the same level-5 interrupt and handler. The next question is how to tell which source triggered the interrupt. One method I can think of is to check the interrupt flags of the 2 sources. I'm wondering if there is any better method to determine the source?

I think I'll post an example project with minimal code of seting up the high-level interrupt when I feel confident. There isn't much examples availalbe on this topic for now, so I believe it could save some effort for the people who have to get low latency and lowe jitter at us level.

Best,
Wayne

Re: Need help on High-Level Interrupts

Posted: Sat May 28, 2022 1:27 am
by ESP_Sprite
No, the interrupt flags are specifically made to figure out what triggered an interrupt. And I agree that it would certainly be helpful if you could publish your example.

Re: Need help on High-Level Interrupts

Posted: Sat May 28, 2022 2:44 pm
by liebman
While this may be too late to help you I used highint 5 to sync a DS3231 RTC with I gps module. It uses highint 5 to detect the active edges of both the GPS and the RTC and computes the delta in microseconds. Code can be found here https://github.com/liebman/esp32-gps-ntp

Re: Need help on High-Level Interrupts

Posted: Sun May 29, 2022 12:51 am
by yuwenpeng
Thank you ESP_Sprite and liebman. You project is great and I can learn from it how to use assembly.
Now I need to divid the pps period (240,000,000 of ccount) into multiple intervals, so I'm trying to divid, or multiply float or double in assemble. I can't find much instruction or example from internet on the float/double divid/multiply operation.
So I disassemble the compiled object using xtensa-esp32-elf-objdump, but the result is completely different from what I expected. I can't find any float/double or divid operation in the assembly.
Did I make any mistakes? That just doesn't make sense to me... The C code and the corresponding Assemble is attached below.

app main in C

Code: Select all

void app_main(void)
{
    double s = 130.0;
    double div=123.456;
    double mul =130.3;
    while(true)
    {
        s=s/div;
        div = div + 1;
        s = s * mul;
        mul = mul + 1;

        printf("\n%f.",s);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
Corresponding assemble (generated by calling xtensa-esp32-elf-objdump

Code: Select all

00000000 <app_main>:
   0:	006136        	entry	a1, 48
   3:	000041        	l32r	a4, fffc0004 <app_main+0xfffc0004>
   6:	000051        	l32r	a5, fffc0008 <app_main+0xfffc0008>
   9:	000021        	l32r	a2, fffc000c <app_main+0xfffc000c>
   c:	000031        	l32r	a3, fffc000c <app_main+0xfffc000c>
   f:	0129      	s32i.n	a2, a1, 0
  11:	1139      	s32i.n	a3, a1, 4
  13:	000021        	l32r	a2, fffc0014 <app_main+0xfffc0014>
  16:	000031        	l32r	a3, fffc0018 <app_main+0xfffc0018>
  19:	01c8      	l32i.n	a12, a1, 0
  1b:	11d8      	l32i.n	a13, a1, 4
  1d:	20a220        	or	a10, a2, a2
  20:	03bd      	mov.n	a11, a3
  22:	000081        	l32r	a8, fffc0024 <app_main+0xfffc0024>
  25:	0008e0        	callx8	a8
  28:	0a2d      	mov.n	a2, a10
  2a:	0b3d      	mov.n	a3, a11
  2c:	0000c1        	l32r	a12, fffc002c <app_main+0xfffc002c>
  2f:	0000d1        	l32r	a13, fffc0030 <app_main+0xfffc0030>
  32:	01a8      	l32i.n	a10, a1, 0
  34:	11b8      	l32i.n	a11, a1, 4
  36:	000081        	l32r	a8, fffc0038 <app_main+0xfffc0038>
  39:	0008e0        	callx8	a8
  3c:	01a9      	s32i.n	a10, a1, 0
  3e:	11b9      	s32i.n	a11, a1, 4
  40:	02cd      	mov.n	a12, a2
  42:	03dd      	mov.n	a13, a3
  44:	04ad      	mov.n	a10, a4
  46:	05bd      	mov.n	a11, a5
  48:	000081        	l32r	a8, fffc0048 <app_main+0xfffc0048>
  4b:	0008e0        	callx8	a8
  4e:	0a2d      	mov.n	a2, a10
  50:	0b3d      	mov.n	a3, a11
  52:	0000c1        	l32r	a12, fffc0054 <app_main+0xfffc0054>
  55:	0000d1        	l32r	a13, fffc0058 <app_main+0xfffc0058>
  58:	04ad      	mov.n	a10, a4
  5a:	05bd      	mov.n	a11, a5
  5c:	000081        	l32r	a8, fffc005c <app_main+0xfffc005c>
  5f:	0008e0        	callx8	a8
  62:	0a4d      	mov.n	a4, a10
  64:	0b5d      	mov.n	a5, a11
  66:	02cd      	mov.n	a12, a2
  68:	20d330        	or	a13, a3, a3
  6b:	0000a1        	l32r	a10, fffc006c <app_main+0xfffc006c>
  6e:	000081        	l32r	a8, fffc0070 <app_main+0xfffc0070>
  71:	0008e0        	callx8	a8
  74:	c8a0a2        	movi	a10, 200
  77:	000081        	l32r	a8, fffc0078 <app_main+0xfffc0078>
  7a:	0008e0        	callx8	a8
  7d:	ffe606        	j	19 <app_main+0x19>
The assemble mix with C version is below:

Code: Select all

void app_main(void)
{
   0:	006136        	entry	a1, 48
    double s = 130.0;
    double div=123.456;
    double mul =130.3;
   3:	000041        	l32r	a4, fffc0004 <app_main+0xfffc0004>
   6:	000051        	l32r	a5, fffc0008 <app_main+0xfffc0008>
    double div=123.456;
   9:	000021        	l32r	a2, fffc000c <app_main+0xfffc000c>
   c:	000031        	l32r	a3, fffc000c <app_main+0xfffc000c>
   f:	0129      	s32i.n	a2, a1, 0
  11:	1139      	s32i.n	a3, a1, 4
    double s = 130.0;
  13:	000021        	l32r	a2, fffc0014 <app_main+0xfffc0014>
  16:	000031        	l32r	a3, fffc0018 <app_main+0xfffc0018>
    while(true)
    {
        s=s/div;
  19:	01c8      	l32i.n	a12, a1, 0
  1b:	11d8      	l32i.n	a13, a1, 4
  1d:	20a220        	or	a10, a2, a2
  20:	03bd      	mov.n	a11, a3
  22:	000081        	l32r	a8, fffc0024 <app_main+0xfffc0024>
  25:	0008e0        	callx8	a8
  28:	0a2d      	mov.n	a2, a10
  2a:	0b3d      	mov.n	a3, a11
        div = div + 1;
  2c:	0000c1        	l32r	a12, fffc002c <app_main+0xfffc002c>
  2f:	0000d1        	l32r	a13, fffc0030 <app_main+0xfffc0030>
  32:	01a8      	l32i.n	a10, a1, 0
  34:	11b8      	l32i.n	a11, a1, 4
  36:	000081        	l32r	a8, fffc0038 <app_main+0xfffc0038>
  39:	0008e0        	callx8	a8
  3c:	01a9      	s32i.n	a10, a1, 0
  3e:	11b9      	s32i.n	a11, a1, 4
        s = s * mul;
  40:	02cd      	mov.n	a12, a2
  42:	03dd      	mov.n	a13, a3
  44:	04ad      	mov.n	a10, a4
  46:	05bd      	mov.n	a11, a5
  48:	000081        	l32r	a8, fffc0048 <app_main+0xfffc0048>
  4b:	0008e0        	callx8	a8
  4e:	0a2d      	mov.n	a2, a10
  50:	0b3d      	mov.n	a3, a11
        mul = mul + 1;
  52:	0000c1        	l32r	a12, fffc0054 <app_main+0xfffc0054>
  55:	0000d1        	l32r	a13, fffc0058 <app_main+0xfffc0058>
  58:	04ad      	mov.n	a10, a4
  5a:	05bd      	mov.n	a11, a5
  5c:	000081        	l32r	a8, fffc005c <app_main+0xfffc005c>
  5f:	0008e0        	callx8	a8
  62:	0a4d      	mov.n	a4, a10
  64:	0b5d      	mov.n	a5, a11

        printf("\n%f.",s);
  66:	02cd      	mov.n	a12, a2
  68:	20d330        	or	a13, a3, a3
  6b:	0000a1        	l32r	a10, fffc006c <app_main+0xfffc006c>
  6e:	000081        	l32r	a8, fffc0070 <app_main+0xfffc0070>
  71:	0008e0        	callx8	a8
        vTaskDelay(1000/portTICK_PERIOD_MS);
  74:	c8a0a2        	movi	a10, 200
  77:	000081        	l32r	a8, fffc0078 <app_main+0xfffc0078>
  7a:	0008e0        	callx8	a8
    while(true)
  7d:	ffe606        	j	19 <app_main+0x19>