Prior to this change, if interrupts were disabled during a call to
HardwareSerial::write() when the circular buffer was full, or
HardwareSerial::flush() when the circular buffer was non-empty,
we would loop forever and trip a watchdog timeout.
Prior to this change, the interrupt could fire during initialisation,
necessitating a deep check that the HardwareSerial structure had valid
_tx_buffer or _rx_buffer each time an interrupt occurred.
By keeping uart_t's and HardwareSerial's (txEnabled, _tx_buffer) and
(rxEnabled, _rx_buffer) in sync, we can remove this extra check, as
well as fixing a null pointer dereference if e.g. _tx_buffer allocation
failed and write() was subsequently called.
This is required per the non-OS SDK doc, which states:
"Using non-OS SDK, please do not call any function defined with
ICACHE_FLASH_ATTR in the interrupt handler."
This avoids an "Illegal instruction" exception with epc1 pointing at a valid
instruction (in flash) for one of the moved methods.
Not sure why, but this reduces the occurrence rate of an occasional ~3.25 or
~7μs intercharacter delay, which was interfering with use of the UART to
generate precise timing pulses (such as driving WS2812 LEDs).
"init_data", when non-NULL, is on the heap, and the register_chipv6_phy call
sometimes modifies data in (at least) the offset range [128:249], suggesting
that it is a buffer larger than 128 bytes in size (the size of our
"phy_init_data" buffer). When we use our static buffer (prior to this
change), the call could would overwrite the .rodata section and lead to
undefined behaviour.
To address this, just patch the heap-allocated buffer with our data.
Move phy_init_data to flash as it's now readonly and never modified.
_verifyHeader is called before the beginning of the update progress to verify the first byte using peek
_verifyEnd is called on the end before the eboot command is written to verify first byte + flash config
add missing _reset() on timeout
Testing:
- Boot tested, ran basic serial I/O code
Notes:
- Before this change, there are instruction like "s32i.n <reg>, <this>, <_begin>" in the
disassembled output, followed by an overwrite if <reg> turns out to be _bufend.
After this change, there is only one store instruction to <_begin> per function.
This avoids a race where the interrupt handler detects an empty _tx_buffer
just before we write data into it.
Note that commit d6f62943d4b511e7d5fe6147096c8979890416f5 works around
this race when data is continually added to _tx_buffer in the hung state.
We revert that change here as the race should no longer occur.
Testing performed:
- set UART_CONF1.txfifo_empty_thrhd=0x70 (which exacerbates the issue)
- generate a ~240 byte burst of data, sent in back-to-back Serial1.write(, 4)
calls, optionally followed by a Serial1.flush()
Test results:
- before this change, observe occasional unsent data and hang in flush()
(if used).
- after this change, data is sent as expected.