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

calling millis() in an interrupt service routine causes a panic #2758

Closed
judge2005 opened this issue May 8, 2019 · 12 comments
Closed

calling millis() in an interrupt service routine causes a panic #2758

judge2005 opened this issue May 8, 2019 · 12 comments

Comments

@judge2005
Copy link
Contributor

Compile and run the code below - you will get a panic in the serial monitor. This is surprising, given that millis() is tagged with IRAM_ATTR. If I call esp_timer_get_time(), there is no crash. I am using release 1.0.2

#include "Arduino.h"
#include "esp_spi_flash.h"
#include "nvs.h"
#include "nvs_flash.h"

hw_timer_t *timer;

void IRAM_ATTR isr() {
	millis();
}

void setup() {
	Serial.begin(115200);

    nvs_flash_init();

    timer = timerBegin(0, 80, true); // timer_id = 0; divider=80; countUp = true; So at 80MHz, we have a granularity of 1MHz
	timerAttachInterrupt(timer, isr, true); // edge = true
	timerAlarmWrite(timer, 100, true); // Period = 100 micro seconds
	timerAlarmEnable(timer);
}

void loop() {
	const uint32_t n = SPI_FLASH_SEC_SIZE;
	uint32_t val_read;
	for (uint32_t offset = 0; offset < n; offset += 4) {
		if (spi_flash_read(offset, (uint8_t *) &val_read, 4) != ESP_OK) {
			break;
		}
	}

	delay(10);
}
@atanisoft
Copy link
Collaborator

Do you have a back trace? I suspect it is crashing in loop() and not in isr(). When the ISR fires, spi_flash disables cache and which will lead to crashes when you attempt to access flash.

@judge2005
Copy link
Contributor Author

I don't get a stack trace. However, as I said I can call esp_timer_get_time(), which is what millis() calls, with no issues.

This is what I have in the console:

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 188777542, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
Guru Meditation Error: Core  1 panic'ed (Cache disabled but cached memory region accessed)
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400ea30c: bad00bad bad00bad bad00bad
Guru Meditation Error: Core  1 panic'ed (IllegalInsGuru Meditation Error: Core  1 panic'ed (Unhandled debug exception)
Debug exception reason: Stack canary watchpoint triggered (loopTask) 
Guru Meditation Error: Core  1 panic'ed (Guru Meditation Error: Core  1 panic'ed (Guru Meditation Error: Core  1 panic'ed (
``

@atanisoft
Copy link
Collaborator

Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed)

This line indicates that spi_flash disabled cache but something was accessed outside of the IRAM area. About the only thing I can think of is the 1000ULL but that should be compiled in as a constant. Can you try swapping millis() with micros() ?

Also try disabling the flash access in loop() (separate test) to confirm if that is playing a factor.

@judge2005
Copy link
Contributor Author

judge2005 commented May 8, 2019 via email

@atanisoft
Copy link
Collaborator

micros() works. How do I disable flash access for the other test?

Interesting that micros() works but millis() doesn't. @me-no-dev thoughts?

As for disabling flash access, comment out the code in loop(), specifically spi_flash_read()

@judge2005
Copy link
Contributor Author

judge2005 commented May 8, 2019 via email

@judge2005
Copy link
Contributor Author

It looks like this is because 64 bit divisions call a function (I tried a test which happened to give a compiler error that lead me to this conclusion).

The GCC C compiler generates code that calls functions in the libgcc library to implement the / and % operations with 64-bit operands on 32-bit CPUs

So, that function is not tagged with IRAM_ATTR and so is not safe to call from an ISR. I will figure out how to do this division myself - just need to the source to that library!

@stickbreaker
Copy link
Contributor

As a work around you might use TickType_t xTaskGetTickCountFromISR(); TickType_t is a uint32_t

I use it in the I2C driver that runs in an Interrupt context and supports iCache disabled.

Chuck.

@judge2005
Copy link
Contributor Author

Thanks @stickbreaker, I will look into that. To close the loop on this, it is definitely caused by GCC generating a call to a library. Possibly, if the optimization level was modified for speed, it might not do that. For your edification, here is some code that works - I'm not claiming it is pretty, but it might actually be faster than the GCC library version. The divs10 code is adapted from the 32 bit version here. Adapting the divs1000 code in that paper to 64 bits is beyond me. Anyway, here is the code:

#include "Arduino.h"
#include "esp_spi_flash.h"
#include "nvs.h"
#include "nvs_flash.h"

hw_timer_t *timer;

// Code adapted for 64 bits from https://www.hackersdelight.org/divcMore.pdf
int64_t IRAM_ATTR divs10(int64_t n) {
	int64_t q, r;
	n = n + (n >> 63 & 9);
	q = (n >> 1) + (n >> 2);
	q = q + (q >> 4);
	q = q + (q >> 8);
	q = q + (q >> 16);
	q = q + (q >> 32);
	q = q >> 3;
	r = n - q * 10;
	return q + ((r + 6) >> 4);
// return q + (r > 9);
}

int64_t IRAM_ATTR divs1000(int64_t n) {
	return divs10(divs10(divs10(n)));
}

unsigned long IRAM_ATTR my_millis()
{
	return divs1000(esp_timer_get_time());
}

void IRAM_ATTR isr() {
	my_millis();
}

void setup() {
	Serial.begin(115200);

	nvs_flash_init();

	timer = timerBegin(0, 80, true); // timer_id = 0; divider=80; countUp = true; So at 80MHz, we have a granularity of 1MHz
	timerAttachInterrupt(timer, isr, true);// edge = true
	timerAlarmWrite(timer, 100, true);// Period = 100 micro seconds
	timerAlarmEnable(timer);
}

void loop() {
	const uint32_t n = SPI_FLASH_SEC_SIZE;
	uint32_t val_read;
	for (uint32_t offset = 0; offset < n; offset += 4) {
		if (spi_flash_read(offset, (uint8_t *) &val_read, 4) != ESP_OK) {
			break;
		}
	}

	delay(10);
}
`

@me-no-dev
Copy link
Member

done :)

@judge2005
Copy link
Contributor Author

judge2005 commented Jun 9, 2019 via email

@im-pro-at
Copy link
Contributor

Interesting ... Gcc can do a lot of unexpected thing!
The jump tables can be turned off by this flag:
gcc -g -v -fno-jump-tables SO.c

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

5 participants