Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interruptions + SPIFFS + std::bind crash #7788

Closed
Bacto opened this issue Dec 22, 2020 · 2 comments
Closed

Interruptions + SPIFFS + std::bind crash #7788

Bacto opened this issue Dec 22, 2020 · 2 comments

Comments

@Bacto
Copy link

Bacto commented Dec 22, 2020

Platform

  • Hardware: ESP8285
  • Core Version: framework-arduinoespressif8266 3.20704.0 (2.7.4)
  • Development Env: Platformio
  • Operating System: MacOS
  • Module: Wemos D1 mini r2

Problem Description

Hi,

I'm trying to use interruptions in a class and use std::bind(&...::..., this) for this purpose.
Unfortunately, when I try to access to the flash using LittleFS, the ESP crashes:

Exception (0):
epc1=0x40209474 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000

Here is the decoded stack trace:

0x401005e9: interrupt_handler at ??:?
0x40100528: interrupt_handler at ??:?
0x401059f8: Cache_Read_Enable_2 at ??:?
0x40105fc1: spi_flash_read at ??:?
0x4010243b: rcReachRetryLimit at ??:?
0x4010261c: rcReachRetryLimit at ??:?
0x40102ade: wDev_ProcessFiq at ??:?
0x40105fbc: spi_flash_read at ??:?
0x40100fa1: __wrap_spi_flash_read at ??:?
0x40208f0d: EspClass::flashRead(unsigned int, unsigned int*, unsigned int) at ??:?
0x402081c4: flash_hal_read(unsigned int, unsigned int, unsigned char*) at ??:?
0x40102b18: wDev_ProcessFiq at ??:?
0x402057be: littlefs_impl::LittleFSImpl::lfs_flash_read(lfs_config const*, unsigned int, unsigned int, void*, unsigned int) at ??:?
0x4020132c: lfs_bd_read$isra$12 at lfs.c:?
0x4020130c: lfs_bd_read$isra$12 at lfs.c:?
0x402014c2: lfs_dir_fetchmatch at lfs.c:?
0x40100601: interrupt_handler at ??:?
0x40201e0a: lfs_dir_find at lfs.c:?
0x40202ae0: lfs_dir_find_match at lfs.c:?
0x40204a18: lfs_file_opencfg at ??:?
0x4020c1b4: _strdup_r at /home/earle/src/esp-quick-toolchain/repo/newlib/newlib/libc/string/strdup_r.c:11
0x40100ce6: malloc at ??:?
0x40100a27: umm_free_core at umm_malloc.cpp:?
0x40204c50: lfs_file_open at ??:?
0x402062ff: littlefs_impl::LittleFSImpl::open(char const*, fs::OpenMode, fs::AccessMode) at ??:?
0x40201c90: lfs_dir_getgstate at lfs.c:?
0x4020530b: std::_Function_base::_Base_manager<std::_Bind<std::_Mem_fn<void (Sandbox::*)()> (Sandbox*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) at ??:?
0x402069a8: fs::FS::open(char const*, char const*) at ??:?
0x40100100: interruptFunctional(void*) at ??:?
0x40100caf: free at ??:?
0x4020fddd: operator delete(void*) at /workdir/repo/gcc/libstdc++-v3/libsupc++/del_op.cc:48
0x40205401: loop at ??:?
0x40205384: Sandbox::init() at ??:?
0x4010048d: digitalWrite at ??:?
0x402053d8: setup at ??:?
0x4020773c: loop_wrapper() at core_esp8266_main.cpp:?
0x401002e5: cont_wrapper at ??:?

Here is a MCVE:

#include "Arduino.h"
#include <LittleFS.h>
#include <FunctionalInterrupt.h>

// ---------

class Sandbox {
  public:
    void init();

  private:
    void ICACHE_RAM_ATTR interruptHandler();
};

// ---------

void Sandbox::init() {
  // Note: use a pin where there is a lot data (I have 8000 changes/second)
  pinMode(D7, INPUT);
  attachInterrupt(D7, std::bind(&Sandbox::interruptHandler, this), CHANGE);
}

void ICACHE_RAM_ATTR Sandbox::interruptHandler() {
}

// ---------


Sandbox sandbox;

void setup() {
  Serial.begin(74880);
  LittleFS.begin();
  sandbox.init();
}

void loop() {
  // noInterrupts();
  File fd = LittleFS.open("/test", "w");
  fd.close();
  // interrupts();
  delay(1000);
}

Note that it happens when I have some interruptions (8000/seconds in my case but it should crashed with less I suppose).

When I suspend interrupts during the FS access, the ESP doesn't crash.

When I declare interruptHandler as static and don't use std::bind it works too (but I'll have to declare all my variables as static which I would like to avoid).

I tried a lot of things to find a solution but nothing worked and I would like to avoid to use a plain classical function as a callback to attachInterrupt.

If you have any idea... 🙏

@devyte
Copy link
Collaborator

devyte commented Dec 23, 2020

You can't use std::bind and interrupts, they don't mix. If memory serves, it's because bind creates an anonymous class that wraps your call in its operator()() method. All code in an ISR must be in IRAM (readthedocs), but this wrapper isn't, so you may get a crash.
Unfortunately, I think it's the only case where moving operator()() to IRAM isn't feasible. You can't use a link time pattern, and you can't decorate the method with the IRAM attr.

Instead, I suggest either std::function (there's a pattern in place to handle the case) or a lambda with the IRAM attr (requires gcc 10.x, so latest master branch. I'm not sure this has been tried for interrupts, it's a rather new thing thanks to the new gcc version merged recently, but it should work).

Closing due to not supported.

@devyte devyte closed this as completed Dec 23, 2020
@Bacto
Copy link
Author

Bacto commented Dec 23, 2020

Many thanks @devyte for your reply!
Unfortunately it is way beyond my C++ skills, so very difficult to me.

I have tried with the master branch, a lambda and IRAM atttribute but still got a crash:

#include "Arduino.h"
#include <LittleFS.h>
#include <FunctionalInterrupt.h>

// ---------
class Sandbox {
  public:
    void init();
    void loop();
    volatile uint32_t counter = 0;

  private:
};

// ---------

void Sandbox::loop() {
  Serial.println(counter);
}

void Sandbox::init() {
  // Note: use a pin where there is a lot data (I have 8000 changes/second)
  pinMode(D7, INPUT);
  attachInterrupt(D7, [this](void)ICACHE_RAM_ATTR{ counter++; }, CHANGE);
}

// ---------

Sandbox sandbox;

void setup() {
  Serial.begin(74880);
  LittleFS.begin();
  sandbox.init();
}

void loop() {
  File fd = LittleFS.open("/test", "w");
  fd.close();

  sandbox.loop();
  delay(1000);
}

For reference, I use platformio and had to add this to platformio.ini to use the master branch:

platform_packages =
  platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
  [email protected]

So I got the following packages:

PACKAGES:
 - framework-arduinoespressif8266 3.0.0-dev+sha.e25ad86
 - tool-esptool 1.413.0 (4.13)
 - tool-esptoolpy 1.20800.0 (2.8.0)
 - tool-mklittlefs 1.203.200522 (2.3)
 - tool-mkspiffs 1.200.0 (2.0)
 - toolchain-xtensa 2.100100.200706 (10.1.0)

I will really appreciate any help as it seems that nobody has a clean solution for now and why not update the documentation accordingly.

I saw multiple forums threads where people are looking for a way to do it an issues too (#1925, arduino/ArduinoCore-API#99, arduino/ArduinoCore-avr#58).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants