Crash in timer interupt when data type "float" is used

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Sun Feb 26, 2017 8:58 am

I'm seeing a weird crash here when I try to use "float" in a timer ISR.

Example:

Code: Select all

hw_timer_t *timer0;

uint32_t timer0_int = 0;
float timer0_float = 0.0;
double timer0_double = 0.0;

void IRAM_ATTR timer0_intr()  // Interrupt handler for timer 0
{
  timer0_int++;
  timer0_float = timer0_int;
  timer0_double = timer0_int;
}

void setup() {
 
  timer0 = timerBegin(0, 80, true);  // divider 80 = 1MHz
  timerAlarmWrite(timer0, 999, true); // Alarm every 1000 µs, auto-reload
  timerAttachInterrupt(timer0, timer0_intr, true); // attach timer0_inter, edge type interrupt
  timerAlarmEnable(timer0);
  
  Serial.begin(115200);
 
}

void loop() {
  Serial.println(timer0_int);
  delay(100);  
}
This sketch will crash and generate the following output:

Code: Select all

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0008,len:8
load:0x3fff0010,len:1760
load:0x40078000,len:6668
load:0x40080000,len:252
entry 0x40080034
0
Guru Meditation Error: Core  1 panic'ed (Unhandled debug exception)
Debug exception reason: BREAK instr 
Register dump:
PC      : 0x40080930  PS      : 0x00060036  A0      : 0x800809c8  A1      : 0x3ffc1100  
A2      : 0x00000000  A3      : 0x00000001  A4      : 0x20000000  A5      : 0x00000000  
A6      : 0x00000400  A7      : 0x00060823  A8      : 0x3ffc2504  A9      : 0x3ffc80f0  
A10     : 0x00000001  A11     : 0x00060023  A12     : 0x80082a29  A13     : 0x3ffc81b0  
A14     : 0x00000003  A15     : 0x00000000  SAR     : 0x00000018  EXCCAUSE: 0x00000001  
EXCVADDR: 0x00000000  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  

Backtrace: 0x40080930:0x3ffc1100 0x400809c8:0x3ffc11e0 0x40081651:0x3ffc1200

CPU halted.
The weird part is, if I comment out this line:

// timer0_float = timer0_int;

then the sketch will run without problems, despite a double precision variable being accessed

So "float" will crash, but "double" is OK to use?

Cheers
Hans

P.S: I'd rather be using floats, they're quite a bit faster than double.

ESP_igrr
Posts: 2071
Joined: Tue Dec 01, 2015 8:37 am

Re: Crash in timer interupt when data type "float" is used

Postby ESP_igrr » Sun Feb 26, 2017 2:18 pm

Floats use the FPU while doubles are calculated in software. For reasons, using the FPU inside an interrupt handler is currently not supported.

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Re: Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Sun Feb 26, 2017 2:37 pm

Thanks for the quick answer, good to know.

I'll use doubles (sparingly) or integer math in the meantime.

Is the problem fixable?


Cheers
Hans Dorn

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: Crash in timer interupt when data type "float" is used

Postby ESP_Sprite » Mon Feb 27, 2017 1:29 am

Yes, it is solvable: it means messing with the asssembler in the coprocessor handler to add the code to store the FPU state when an interrupt happens and restoring it when the interrupt is done. I have it on my list, but it's fairly low-priority: you can expect a fix evenrually, but I don't know the timeframe for that.

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Re: Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Wed Mar 01, 2017 5:23 am

I managed to switch on the FPU in my ISR by doing the following:

Code: Select all

uint32_t timer0_int = 0;
float timer0_float = 0.0;

uint32_t cp0_regs[18];

void IRAM_ATTR timer0_intr()  // Interrupt handler for timer 0
{

  // enable FPU
  xthal_set_cpenable(1);
  // Save FPU registers
  xthal_save_cp0(cp0_regs);
  
  timer0_int++;
  timer0_float = timer0_int * 1.1111111111;
  timer0_double = timer0_int;

  // Restore FPU
  xthal_restore_cp0(cp0_regs);
  // and turn it back off
  xthal_set_cpenable(0);

}
Will this break anything?
Do I have to save the FPU registers, or will the FPU be unusable anyway once FreeRTOS returns to the interrupted task?

Cheers

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: Crash in timer interupt when data type "float" is used

Postby ESP_Sprite » Wed Mar 01, 2017 9:40 am

It may work, if you restore the FPU-enabled state to the same state it was in. Depending on what the interrupted task was doing, it can actually be on or off, and because Xtensa uses lazy switching, this actually contains information about the FPU state itself. It's kinda-sortta here-be-dragons, though: I'd advise you just to use ints and do any float calculation in a task if you can get away with it.

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Re: Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Thu Mar 02, 2017 12:27 am

ESP_Sprite wrote:It may work, if you restore the FPU-enabled state to the same state it was in. Depending on what the interrupted task was doing, it can actually be on or off, and because Xtensa uses lazy switching, this actually contains information about the FPU state itself.
Thanks, I was wondering about what happens if the ISR interrupts a task that is running with an enabled CPU.
It's kinda-sortta here-be-dragons, though: I'd advise you just to use ints and do any float calculation in a task if you can get away with it.
I hear you. Me being curious, I'll try to make stuff break in interesting ways first, and maybe learn something this way.

Cheers
Hans

P.S:
I just found that accessing a floating point literal in your code will clear cpenable, inducing a hefty penalty in wasted cycles.
Keeping your constants stored like this:

Code: Select all

   DRAM_ATTR float f3 = 1.0001;
will prevent GCC from mis-optimizing your code.

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Re: Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Fri Mar 03, 2017 2:20 am

Ok here we go :)


This version works nicely for me:

Code: Select all

uint32_t timer0_int = 0;
float timer0_float = 0.0;
DRAM_ATTR float timer0_k = 1.111111;

uint32_t cp0_regs[18];

void IRAM_ATTR timer0_intr()  // Interrupt handler for timer 0
{

  // get FPU state
  uint32_t cp_state = xthal_get_cpenable();
  
  if(cp_state) {
    // Save FPU registers
    xthal_save_cp0(cp0_regs);
  } else {
    // enable FPU
    xthal_set_cpenable(1);
  }
  
  timer0_int++;
  timer0_float = timer0_int * timer0_k;

  if(cp_state) {
    // Restore FPU registers
    xthal_restore_cp0(cp0_regs);
  } else {
    // turn it back off
    xthal_set_cpenable(0);
  }

}
I let a long running float calculation run in the main loop and verified that
either failing to return the cpenable flag in the state I found it,
or failing to save and restore the FPU registers will make the main loop fail.

Extended precision integer math doesn't look like much fun on xtensa, because the CPU doesn't have any arithmetic flags...
(for performance reasons I guess)


Thx for the hint.

Cheers
Hans

P.S:
I know I'm on my own here when I do something in my code that causes the FPU to become disabled,
but I'd rather cope with this and keep using the fast FPU

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: Crash in timer interupt when data type "float" is used

Postby ESP_Sprite » Fri Mar 03, 2017 4:49 am

Ah, you probably want to save/restore the state of the FPU regardless if it's enabled or disabled. The way Xtensa FreeRTOS works is that if a task that uses the FPU is switched out, the FPU gets disabled but the data stays in the FPU registers. If the same task is later switched in, the FPU only needs to be re-enabled. If another task uses the FPU as well, the coprocessor exception will take care of saving/loading states at that time. So, when the FPU is used in a program, the contents of the registers are always 'live' and can never be discarded.

Hans Dorn
Posts: 62
Joined: Tue Feb 21, 2017 2:21 am

Re: Crash in timer interupt when data type "float" is used

Postby Hans Dorn » Fri Mar 03, 2017 4:59 am

OK, will do.


I'm always trying to shave off a few cycles, as IME there are never enough.

Cheers

Who is online

Users browsing this forum: Google [Bot] and 102 guests