I'm trying to write a driver for a very timing sensitive device. I got it 99% working, however then I came across a weird behavior of the ESP8266. After hours of debugging it seems I cannot find the root cause.
I tried to simplify the issue as much as possible, and finally came out with the code below.
Basically it's a simple in-line assembler routine that
- disables interrupts
- switches GPIO5 high (on)
- executes 999 NOPs
- switches GPIO5 high (low)
Using a scope, I can verify that the code runs well enough. With the ESP8266 operating at 80MHz, one CPU cycle is 12.5ns, and the 1000 cycles between the GPIO flips come out to exactly 12.5us. This can be exactly seen on the scope.
BUT: every now and then, the pulse as a width of approximately 24us. If I execute the output_pulse() function via a timer every 10ms, this happens roughly every couple of seconds, so every couple of hundred executions.
This is no general timinig inaccuracy - I can definitely say that in 99.x% of the cases, the pulse with is exactly 12.5us, and then, for a single instance, it is about 24us. Other times do not exist.
Since I disabled all interrupts (rsil instruction) at the beginning of the procedure, I have no idea what could interrupt the function. And no, it doesn't have anything to do with the rsil stuff, I can use taskDISABLE_INTERRUPTS() instead, or even the XTOS_DISABLE_ALL_INTERRUPTS macro, no difference whatsoever.
Any help is greatly appreciated.
Thanks,
Roland
static void __attribute__ ((noinline)) output_pulse()
{
uint32_t set_gpio_addr = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TS_ADDRESS;
uint32_t clear_gpio_addr = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TC_ADDRESS;
uint32_t gpio = BIT5;
uint32_t intlevel;
__asm__ __volatile__ (
"rsil %0, 3\n" // disable all interrupts
"s32i %1, %2, 0\n" // switch on GPIO5 (write to GPIO_OUT_W1TS)
"nop\n"
"nop\n"
{..... 995 more "nop\n", cut out here for readability ....}
"nop\n"
"nop\n"
"s32i %1, %3, 0\n" // switch off GPIO5 (write to GPIO_OUT_W1TC)
"wsr.ps %0" // restore interrupts
: "=&r" (intlevel)
: "r" (gpio), "r" (set_gpio_addr), "r" (clear_gpio_addr)
: "memory"
);
}