Skip to content

1.3.0

Choose a tag to compare

@maxgerhardt maxgerhardt released this 05 Sep 20:12
· 75 commits to main since this release

Over-the-air updates

Flash a sketch over the network. The board appears in the Arduino IDE under
Tools → Port as a network port, and PlatformIO takes upload_protocol = espota. It speaks the espota protocol, so tools/espota.py works by hand too.

ArduinoOTA and Updater are ported from arduino-pico, keeping their LGPL
headers and attribution, and adapted only where the storage differs.
arduino-pico stages a new image on LittleFS and lets its bootloader apply it.
This part has no bootloader and no second slot — the sketch region is 912 KB of
a 960 KB part — so the image is held in RAM until it is complete and its MD5
checks out, and only then is it committed over the running sketch by a routine
that runs from ITCM because it erases the flash it is stored in.

Two consequences worth knowing before building on it:

  • An over-the-air image is capped by the heap, not the flash — around
    430 KB against the 912 KB a probe can write. ArduinoOTA.maxImageSize()
    reports what fits right now, and an oversized upload is refused at the invite
    rather than two thirds of the way through.
  • The commit is not power-fail safe. Between the first erase and the last
    program the sketch region is neither image. Verifying the MD5 before erasing
    keeps that window as small as it can be; it cannot close it.

The build now also produces firmware_ota.bin — the sketch half of the image,
without the V3F boot stub — and the Updater rejects a full firmware.bin sent
in its place, which is the mistake that would otherwise put the boot stub where
the sketch belongs.

Verified end to end on hardware, repeatedly: transfer, MD5, commit, reboot,
running the new firmware with the network back up.

Next-line branch prediction is off, and that is a bug fix

corecfgr bit 15 makes the V5F mispredict and resume execution at an address
it was never sent to
. It presented as a wild pointer inside lwIP on every
boot, and moved or vanished with any unrelated change — a compiler flag, an
added string, an enabled assert.

What it actually did: execution ran off from the middle of memp_malloc() into
the middle of memp_free(), 0xF8 further on, between two byte-identical
instructions — so lwIP's pool index was computed twice and the second result
indexed hundreds of kilobytes past the table into unprogrammed flash.

Measured three ways on one unchanged build:

I-cache NLP result
on on faults, deterministically
off on runs — NLP does nothing without the cache
on off runs, at full cached speed

The middle row is why this looks like a cache bug and is not one. The cache
stays on; the predictor does not. docs/hazards.md has the full account.

Other fixes

  • A crash guard that wedged the debug probe. When the V5F faulted
    repeatedly, the V3F printed "reflash, or reset twice to try again" and then
    spun in for (;;) {} — which wedges the WCH-Link, so the escape hatch needed
    the probe it had locked out. Now __WFI(), and the probe attaches to a
    fault-guarded board.
  • ltoa / utoa / ultoa, which the Arduino API requires a core to supply
    and newlib does not have. String(millis()) had never linked.
  • -mno-save-restore, so nothing in ITCM calls into flash. -msave-restore
    put __riscv_save_4 into the OTA committer's prologue, and GCC turned its
    deliberate word-at-a-time copy back into a memcpy — two calls into the
    flash being erased, neither of them in the source. tools/check_itcm.py now
    fails the build on any call out of ITCM.
  • Fault records now capture ra, a0 and a1. The handler is naked, so
    these are the faulting context's real values; they are what identified the
    predictor bug.
  • lwIP asserts and corruption detectors behind
    -DCH32H4_LWIP_ASSERT_CONSOLE, routed to the raw UART.

Since 1.2.0 also

mDNS with _arduino._tcp advertisement; multicast reception that works
(hash-table filter with the bit-reversed CRC the MAC actually wants); a single
ITCM flash driver instead of three copies of the register sequences; automatic
parking of the other core on any flash write; Keyboard, Mouse and
Joystick over USB HID; a CH32H4 library with a cross-core mutex;
LED_BUILTIN, LED1 and LED2; examples for every library that had none;
mbedTLS and lwIP as ordinary libraries, so board_build.tls and
board_build.network are gone.