Tracking _written was required on AVR devices to work around a hardware
limitation when implementing flush(). The ESP8266 doesn't have the same
issue, so we can remove the tracking and make write() more lightweight.
The cost is a minor increase in work done in flush() when write() was not
previously called, but this should be much rarer than individual character
writes.
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.