ESP32-S3 GPIO High Level Interrupt not working in PIO

Hi,

I need to process a worst case 2MHz manchester signal (1 Mbit) and want to give it a try with plain ESP32-S3 using High Level Assembler interrupts. I found a post that implements this scheme (ESP-IDF) and I am strugling to port it to PIO.
There is a chance the posted code has a flaw but I am not discarding that despite of being able to compile and upload, the code is crashing one time after the other.

This is the URL
High Level Interrupt Handler

This is how my PIO files look (some modifications based on other URL’s)

platformio.ini

[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = espidf
monitor_speed = 115200

In folder src I have 3 files

-CMakeLists.txt
-highint5.S
-main.c

CMakeLists.txt

FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})
target_link_libraries(${COMPONENT_TARGET} "-u ld_include_xt_highint5")

highint5.S

#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>

//#include "freertos/xtensa_context.h" // deprecated now <xtensa_context.h>
#include <xtensa_context.h>

#include "esp_private/panic_reason.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/gpio_reg.h"
#include "soc/dport_reg.h"

#define L5_INTR_STACK_SIZE  12
#define L5_INTR_A15_OFFSET   0
#define L5_INTR_A14_OFFSET   4
#define L5_INTR_A13_OFFSET   8

    .data
_l5_intr_stack:
    .space      L5_INTR_STACK_SIZE

    .section .iram1,"ax"
    .global     xt_highint5
    .type       xt_highint5,@function
    .align      4
xt_highint4:

    movi    a0, _l5_intr_stack // assuming a0 saved restored by EXCSAVE_5
    s32i    a15, a0, L5_INTR_A15_OFFSET
    s32i    a14, a0, L5_INTR_A14_OFFSET
    s32i    a13, a0, L5_INTR_A13_OFFSET

    // Clear interupt on GPIO17
    movi    a14, GPIO_STATUS_W1TC_REG
    movi    a15, (1 << 17)
    s32i    a15, a14, 0
    memw

    // Toggle output reg (GPIO18)
    movi    a13, GPIO_OUT_W1TS_REG
    movi    a14, GPIO_OUT_W1TC_REG
    movi    a15, (1 << 18)
    s32i	a15, a13, 0
    s32i	a15, a14, 0

    // Increment intCounter
    movi    a14, intCounter
    l32i    a15, a14, 0
    addi    a15, a15, 1
    s32i    a15, a14, 0
    memw

    l32i    a15, a0, L5_INTR_A15_OFFSET
    l32i    a14, a0, L5_INTR_A14_OFFSET
    l32i    a13, a0, L5_INTR_A13_OFFSET

    rsr     a0, EXCSAVE_5 // restore a0
    rfi     5

    .global ld_include_xt_highint5
ld_include_xt_highint5:

main.c (had to include more #include files to compile) (changed “intr_matrix_set” to function “esp_intr_alloc” as recommended in other posts)

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "spi_flash_mmap.h"
#include "driver/gpio.h"
#include "esp_intr_alloc.h"
#include "freertos/queue.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"

#include "xtensa/core-macros.h"
#include "soc/periph_defs.h"
#include "esp_log.h"
#include "esp_rom_gpio.h"
#include "rom/ets_sys.h"

#include "soc/gpio_struct.h"

#include "esp_err.h"
#include "esp_attr.h"
#include "hal/cpu_hal.h"

#include "soc/gpio_reg.h"

#define GPIO_OUTPUT_IO_0    18
#define GPIO_OUTPUT_PIN_SEL (1ULL<<GPIO_OUTPUT_IO_0)
#define GPIO_INPUT_IO_0     17
#define GPIO_INPUT_PIN_SEL  (1ULL<<GPIO_INPUT_IO_0)

#define INTR_NUM 31
#define CPU_CORE 0

volatile int intCounter = 0;
volatile int regVal = 0 ;

static void gpio_task_example(void* arg) {

    intr_handle_t handle;
    esp_err_t err;

    // setup
	gpio_config_t io_conf;

	//disable interrupt
	io_conf.intr_type = GPIO_INTR_DISABLE;
	//set as output mode
	io_conf.mode = GPIO_MODE_OUTPUT;
	//bit mask of the pins
	io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
	//disable pull-down mode
	io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
	//disable pull-up mode
	io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
	//configure GPIO with the given settings
	gpio_config(&io_conf);


	//interrupt of rising edge
	io_conf.intr_type = GPIO_INTR_POSEDGE;
	//set as input mode
	io_conf.mode = GPIO_MODE_INPUT;
	//bit mask of the pins
	io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
	gpio_config(&io_conf);

	gpio_set_level(GPIO_OUTPUT_IO_0, 0);

	ESP_INTR_DISABLE(INTR_NUM);

    err = esp_intr_alloc(INTR_NUM, ESP_INTR_FLAG_LEVEL5|
ESP_INTR_FLAG_IRAM, NULL, NULL, &handle); // Must be LEVEL5
    if (err != ESP_OK) {
        printf("Failure: could not install ISR, %d(0x%x)\n", err, err);
        while (true);
    }

	ESP_INTR_ENABLE(INTR_NUM);

	while(1){

		printf("Task waiting for interrupts, count is %i\n", intCounter);
		printf("GPIO Reg value = %X", regVal);
		vTaskDelay(20000);

	}
}

void app_main(void) {
    printf("This tests the interrupt latency, plug in a fungen to pin 17 and observe on pin 18!\n");

    xTaskCreatePinnedToCore(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL, 1);
    while(true) {}

}

I am feeding a square wave on GPIO17 and expecting on the Oscilloscope at least a short pulse on GPIO18.
The code starts printing a lot of stuff (probably a debug level of IDF) and crashes inmediately.

  1. To my sdkconfig.project name y removed
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 and
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1

as I was getting watchdog crashes too

  1. On CMakeLists.txt I added

target_link_libraries(${COMPONENT_TARGET} "-u ld_include_xt_highint5")

as I understand, it is important for the linker

I have no clue if platformio.ini requires a flag or something else to produce an executable that behaves different or if there is a bug in the code or if I introduced a bug.

Any help or clue would be appreciated!

Hi,

I finally got it working. The bad thing is that I am not 100% sure what did the change/trick, but there was a huge error in the name of the assembler code entry point, xt_highint4: (must be xt_highint5:) and I think I had to remove the .pio folder (and maybe something else). I will copy the code for anyone interested. I am to lazy to compare this code with the original post to figure out.
I am able to react to a square wave of frequency 2,5 MHz with a latency of about 330ns between input square wave rising edge and toggle pulse start (GPIO18). And the toggle pulse is only 70ns wide. I will add the final code in a following reply.
Each square on the Oscilloscope is 100ns wide.

1 Like

Here is the code

Assembler

#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>

//#include "freertos/xtensa_context.h" // deprecated now <xtensa_context.h>
#include <xtensa_context.h>

#include "esp_private/panic_reason.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/gpio_reg.h"
#include "soc/dport_reg.h"

#define L5_INTR_STACK_SIZE  12
#define L5_INTR_A15_OFFSET   0
#define L5_INTR_A14_OFFSET   4
#define L5_INTR_A13_OFFSET   8

    .data
_l5_intr_stack:
    .space      L5_INTR_STACK_SIZE

    .section .iram1,"ax"
    .global     xt_highint5
    .type       xt_highint5,@function
    .align      4
xt_highint5:

    movi    a0, _l5_intr_stack // assuming a0 saved in EXCSAVE_5
    s32i    a15, a0, L5_INTR_A15_OFFSET
    s32i    a14, a0, L5_INTR_A14_OFFSET
    s32i    a13, a0, L5_INTR_A13_OFFSET

    // Clear interupt on GPIO17
    movi    a14, GPIO_STATUS_W1TC_REG
    movi    a15, (1 << 17)
    s32i    a15, a14, 0
    memw

    // Toggle output reg (GPIO18)
    movi    a13, GPIO_OUT_W1TS_REG
    movi    a14, GPIO_OUT_W1TC_REG
    movi    a15, (1 << 18)
    s32i	a15, a13, 0
    s32i	a15, a14, 0

    // Increment intCounter
    movi    a14, intCounter
    l32i    a15, a14, 0
    addi    a15, a15, 1
    s32i    a15, a14, 0
    memw

    l32i    a15, a0, L5_INTR_A15_OFFSET
    l32i    a14, a0, L5_INTR_A14_OFFSET
    l32i    a13, a0, L5_INTR_A13_OFFSET

    rsr     a0, EXCSAVE_5 // restore a0
    rfi     5

    .global ld_include_xt_highint5
ld_include_xt_highint5:

C code

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "soc/periph_defs.h"
#include "rom/ets_sys.h"

#define GPIO_OUTPUT_IO_0    18
#define GPIO_OUTPUT_PIN_SEL (1ULL<<GPIO_OUTPUT_IO_0)
#define GPIO_INPUT_IO_0     17
#define GPIO_INPUT_PIN_SEL  (1ULL<<GPIO_INPUT_IO_0)

#define INTR_NUM 16
#define CPU_CORE 0

int intCounter = 0;

static void gpio_task_example(void* arg) {

    intr_handle_t i_handle;
    esp_err_t err;

    // setup
	gpio_config_t io_conf;

	//disable interrupt
	io_conf.intr_type = GPIO_INTR_DISABLE;
	//set as output mode
	io_conf.mode = GPIO_MODE_OUTPUT;
	//bit mask of the pins
	io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
	//disable pull-down mode
	io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
	//disable pull-up mode
	io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
	//configure GPIO with the given settings
	gpio_config(&io_conf);


	//interrupt of rising edge
	io_conf.intr_type = GPIO_INTR_POSEDGE;
	//set as input mode
	io_conf.mode = GPIO_MODE_INPUT;
	//bit mask of the pins
	io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
	gpio_config(&io_conf);

	gpio_set_level(GPIO_OUTPUT_IO_0, 0);

    err = esp_intr_alloc(INTR_NUM, 
	                     ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_IRAM, 
						 NULL, 
						 NULL, 
						 &i_handle);
    if (err != ESP_OK) {
        printf("Failure: could not install ISR, %d(0x%x)\n", err, err);
        while (true);     // Die here!
    }

	while (true) {
		printf("Task waiting for interrupts, count is %i\n", intCounter);
		vTaskDelay(2000/portTICK_PERIOD_MS);
	}
}

void app_main(void) {
    printf("This tests the interrupt latency, plug in a fungen to pin 17 and observe on pin 18!\n");

    xTaskCreatePinnedToCore(gpio_task_example, "gpio_task_example", 4096, NULL, 10, NULL, CPU_CORE);
    while(true) {
		vTaskDelay(100/portTICK_PERIOD_MS);
	}
}

Hi,

Calling ESP_INTR_ENABLE(INTR_NUM) after esp_intr_alloc() was a big sin too.
ISR stops working if you keep that line.