- Wed Aug 05, 2015 11:58 pm
#25153
rhaenel wrote:So, here is now the full code to 'get rid' of the NMI and do critical timing stuff. Hope this turns out to be is useful for anyone else:
Hi rhaenel,
Thanks for posting this, it was extremely useful. I had assumed the NMI was pretty non-maskable until I saw these posts!
I did a few experiments of my own last week working on
esp-open-rtos, trying to resolve a problem with dropped packets while interrupts were disabled. Disabling the NMI didn't resolve my problem (it seems frames are ACKed at the WiFi layer even if NMI is off, I was hoping they'd be dropped thereby triggering retries at the WiFi layer). However I did learn a couple of other things that I can share:
- This may be obvious to some, but I didn't notice that the code above clobbers a0 if an NMI comes in during the "timing critical" period. So you can't call any functions as part of the "time critical" stuff, and even simple C may be dangerous depending on register allocation.
The safest thing to do is to probably use one whole __asm__ block for the whole thing (disable->time critical->enable), and list "a0" in the reserved registers for the block so it gets saved/restored correctly by gcc.
- Depending on how long the NMI is disabled for, the ESP may still lock up after NMI is restored. What seems to happen is that during the NMI a sanity check in the lower MAC layer fails and you'll see "lmac.c 576" or "wdev.c 1166" printed, then a watchdog reset. I think this happens if too many WiFi frames are received while the NMI is disabled, some radio-facing buffer overflows and the system can't recover.
On a quiet WiFi network it seems you can disable NMI for the duration of approx 50000-100000 instructions, on a busy WiFi network (simulated by ping flooding the ESP) it's more like 7500. So it's still a lot of instructions to play with, depending on what you need to do. I don't know how that translates to actual times.
- I found a way to disable the NMI without clobbering a0, because there is a method
wDev_MacTim1Arm that triggers the NMI. This function is normally used to schedule the NMI watchdog every 20ms, but when called with delay=1 it triggers the NMI (setting delay=0 races the timer count and misses the NMI sometimes.)
I get the same results calling wDev_MacTim1Arm as with the above code, only can call functions because of no reserved registers needed. This makes it possible to write disableNMI() and enableNMI() functions that could be called from multiple places.
Here's a quick test, adapted from the above:
Code: Select allIRAM void timeCriticalTest()
{
void *nmi_isr;
vPortEnterCritical();
__asm__ __volatile__ (
"j function_entry\n"
".align 128\n"
"vecbase_mod:\n"
"nop\n"
".align 16\n"
"debug_exception_mod:\n"
"nop\n"
".align 16\n"
"nmi_exception_mod:\n"
"rfi 3\n"
"function_entry:\n"
"rsr.vecbase %0\n"
"movi a2, vecbase_mod\n"
"wsr.vecbase a2\n"
: "=r" (nmi_isr)
:
: "a2", "memory"
);
/* Roughly calibrated delay loop.
Count (approx x100 instructions) on first line lets you modify delay duration: */
/* 1000 = ESP will crash with quiet WiFi */
/* 500 = ESP seems stable on quiet WiFi, but instant crash on ping flood */
/* 100 = crashes under ping flood */
/* 75 = couldn't make it crash */
__asm__ __volatile__ (
"movi a2, 500\n"
"movi a3, 1\n"
"loop_top:\n"
".rept 100\n"
"or a3, a3, a3\n"
".endr\n"
"sub a2, a2, a3\n"
"bnez a2, loop_top\n"
:
:
: "a2", "a3"
);
__asm__ __volatile__ (
"wsr.vecbase %0\n" // restore original vecbase
:
: "r" (nmi_isr)
: "memory"
);
sdk_wDev_MacTim1Arm(1);
vPortExitCritical();
}
(Above is code for
esp-open-rtos - based on RTOS SDK 0.9.9 - so it will need some changes to work with the IoT SDK, the wDev_MacTim1Arm() function may be different/missing/renamed in the IoT SDK also.)
One last thing, I saw your post on bbs.espressif asking for ways to run a timer in the NMI. Foogod at the esp8266-re project has found a way to do this as well, take a look at
wDev_MacTimArm & wDev_MacTimSetFunc. In theory these give you a way to run a timer handler in the NMI at a specified timeout, and it's not used for anything else in the 0.9.9 RTOS SDK.
Hope some of this is of interest to someone!