Skip to content

all: Add the Morse Micro Wi-Fi HaLow driver. - #1

Merged
dpgeorge merged 2 commits into
micropython:mainfrom
kwagyeman:add-driver
Sep 15, 2026
Merged

dpgeorge merged 2 commits into
micropython:mainfrom
kwagyeman:add-driver

Conversation

@kwagyeman

Copy link
Copy Markdown
Collaborator

Summary

The drivers/halow/ content from micropython/micropython#19617, reorganised
along the lines of cyw43-driver as discussed there:

  • src/ -- the driver, with the C API renamed mm_halow_* and all
    configuration macros MM_HALOW_*. Everything the driver previously took
    from MicroPython (machine-SPI, the GC-heap root for its private heap, the
    pin-interrupt callback, atomics/ticks/errnos/printf) now arrives through
    the mm_halow_config.h / mm_halow_configport.h contract: the embedder
    supplies atomic sections, a millisecond tick, pin accessors,
    interrupt-context detection, and the mm_halow_port_* functions (SPI
    transport, heap backing, hardware RNG, fallback MAC, optional edge IRQ).
    mmport.h keeps its name -- morselib's own headers include it.
  • lib/mm-iot-sdk -- the Morse Micro MM-IoT-SDK as a submodule, pinned to
    the exact version the hardware numbers in the MicroPython PR were measured
    with (2.12.3).
  • mm_halow.mk -- build fragment for make-based embedders: prebuilt-morselib
    linkage (grouped with the target multilib's libc/libm for -nostdlib
    hosts) and firmware/BCF blob embedding.
  • tests/host -- the allocator suite compiled natively from the real
    sources (233 checks), plus an ASan/UBSan variant.
  • tests/qemu -- the scheduler suite on a Cortex-M55 under QEMU (51
    checks); the context switch is naked asm, so it can't run on the host.
  • tests/hardware -- the on-device throughput/soak harness the driver was
    validated with (20-minute TCP/UDP soaks against a HaLow AP), run by hand.
  • .github/workflows/test.yml -- runs the host and QEMU suites in CI.

Testing

Both suites pass locally: host with gcc -m32 and under ASan/UBSan, QEMU
with arm-none-eabi-gcc 14.3.1 on mps3-an547 (Cortex-M55 needs GCC >= 14).
The MicroPython-side integration (submodule + configport + port glue) is the
follow-up, after which the N6/AE3/RT1062 hardware gauntlet re-runs.

@kwagyeman

Copy link
Copy Markdown
Collaborator Author

@dpgeorge

@dpgeorge

Copy link
Copy Markdown
Member

Looks great!

I'd like to add more CI: code formatting (C and Python), code spell, building under clang. And putting CI related functions in tools/ci.sh like how cyw43-driver does it. I'm happy to do that myself after this PR is merged, but if you want to add it here, please do so.

@kwagyeman

kwagyeman commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

I can add. Give me a second.

@kwagyeman

Copy link
Copy Markdown
Collaborator Author

Almost done, adding a QEMU build for the device and not the host.

@dpgeorge

Copy link
Copy Markdown
Member

No such thing as too much CI 😂

@dpgeorge

Copy link
Copy Markdown
Member

The CI added here doesn't seem to be running and I'm not sure why. It should work...

@kwagyeman

kwagyeman commented Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

@dpgeorge - Okay, finished. I think the PR needs to be merged first, as the CI for the PR doesn't run on itself.

Break this PR into CI edits and code?

@dpgeorge

Copy link
Copy Markdown
Member

I think the PR needs to be merged first, as the CI for the PR doesn't run on itself.

I'm quite certain it should run here...

@dpgeorge

Copy link
Copy Markdown
Member

I checked github settings and the actions are enabled. Not sure what's wrong.

Break this PR into CI edits and code?

Yes, please open a separate PR in this repo to add just the code formatting and spelling workflows. Then I'll merge that first.

@kwagyeman
kwagyeman force-pushed the add-driver branch 2 times, most recently from cf788cc to 8422aef Compare September 15, 2026 01:29
@kwagyeman

Copy link
Copy Markdown
Collaborator Author

Rebased

@kwagyeman

Copy link
Copy Markdown
Collaborator Author

Wait, sending CI stuff, AI just decided to drop it for no reason. Readding...

@kwagyeman
kwagyeman force-pushed the add-driver branch 2 times, most recently from af156dd to 33c722a Compare September 15, 2026 01:54
@kwagyeman

Copy link
Copy Markdown
Collaborator Author

@dpgeorge - Ready

@dpgeorge

Copy link
Copy Markdown
Member

CI is working, nice!

Comment thread tests/host/Makefile Outdated
#
# make build + run
# make asan build + run under ASan/UBSan (catches what the checks miss)
CC ?= gcc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line does nothing because CC is set by make to the default value of cc. So maybe just remove this line.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread tests/qemu/Makefile Outdated
# Cortex-M55 needs arm-none-eabi-gcc 14 or newer (Ubuntu 24.04 ships 13.2,
# which rejects -mcpu=cortex-m55 with hard float). Override CC to point at a
# suitable toolchain if the default is too old.
CC ?= arm-none-eabi-gcc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line does nothing: building with just make doesn't work because CC defaults to cc and is not overridden by this line.

Suggest making this (and the GCC variable below) and unconditional =:

CC = arm-none-eabi-gcc

That gets make working. And still allows overriding it via make CC=foo.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread tests/host/Makefile Outdated
CC ?= gcc
HALOW := ../../src
SDK := ../../lib/mm-iot-sdk/framework/morselib/include
CFLAGS := -m32 -std=c11 -Wall -Wextra -Werror -g -O1 \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these call all just use = (no need for :=).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread tests/qemu/Makefile Outdated
CFLAGS := $(ARCH) -std=c11 -Wall -Wextra -Werror -Wno-unused-parameter -Og -g \
-ffreestanding -fno-common \
-Istub -I$(HALOW) -I../../lib/mm-iot-sdk/framework/morselib/include
LDFLAGS := $(ARCH) -T$(LDSCRIPT) -nostartfiles -Wl,--gc-sections \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these (and others in this file) can all use = instead of :=.

@kwagyeman kwagyeman Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread tests/host/stub/lwip/netif.h Outdated
@@ -0,0 +1,14 @@
// Host-test stub: halow.h embeds these by value, so they need to be complete

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm_halow.h

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_sched.c Outdated
// (the driver task and the UMAC event loop), plus one more for the SDIO/SPI IRQ
// task when the SDIO transport is in use.
#ifndef MM_HALOW_MAX_TASKS
#define MM_HALOW_MAX_TASKS (4)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest renaming this option to MM_HALOW_SCHED_MAX_TASKS`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_sched.c Outdated
// Task turns per mm_halow_sched_run() call.
// Ceiling on any single wait, however long the caller asked for.
#ifndef MM_HALOW_WAIT_MAX_MS
#define MM_HALOW_WAIT_MAX_MS (10000)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest renaming this option to MM_HALOW_SCHED_WAIT_MAX_MS.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_hal.c Outdated
// an unevenly-dividing request yields a *faster* bus (40MHz becomes 50MHz on a
// 200MHz AHB). Check the rate you got.
#ifndef MM_HALOW_SPI_BAUDRATE
#define MM_HALOW_SPI_BAUDRATE (12 * 1000 * 1000)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to mm_halow_config.h.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_hal.c Outdated
// Set by boards that invert the transceiver's BUSY output before it reaches the
// MCU. RESET_N is active low at the transceiver and is not configurable.
#ifndef MM_HALOW_BUSY_INVERTED
#define MM_HALOW_BUSY_INVERTED (0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to mm_halow_config.h.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_libc.c Outdated
* its own heap, so nothing else needs it. morselib never calls these at run
* time, so the stubs only exist to satisfy the link, and _sbrk deliberately
* fails rather than hand out any of MicroPython's memory. They are weak so that
* a port supplying real implementations takes precedence.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this file does not belong in this driver. It is unfortunate that the mm-iot-sdk links against these, but it will need to be up to the host system (eg MicroPython) to provide them (and at least alif already does).

Instead I suggest removing this file, and moving this comment into the README, under the "Integrating" section.

Otherwise, it might be very confusing when someone integrates this driver, starts calling libc functions and finds they all fail (due to these weak stubs being pulled in behind the scenes).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: osal.c still carries two weak libc stubs (_kill/_getpid) of the same kind as the removed file. I left them since only mm_halow_libc.c was flagged, but they're the same principle — I can pull those out too if the driver needs to carry none.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest removing both those _kill and _getpid stubs. According to the comments they aren't even used.

@kwagyeman kwagyeman Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread mm_halow.mk Outdated
# The transceiver firmware, and optionally a board configuration file holding
# calibration data, are linked in as binary blobs. A board picks its BCF by
# name with MM_HALOW_BCF; the SDK keeps them per chip under morsefirmware.
MM_HALOW_CHIP ?= mm8108

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might also need to be changed to MM_HALOW_CHIPSET to match the C-level config

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kwagyeman
kwagyeman force-pushed the add-driver branch 2 times, most recently from 84c71cd to 3d9ddd7 Compare September 15, 2026 05:32

@dpgeorge dpgeorge left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating. Just a few more comments and it should be good to merge.

Comment thread README.md Outdated
This is a driver for the Morse Micro MM6108/MM8108 802.11ah (Wi-Fi HaLow) transceivers.
A portable driver for the Morse Micro MM6108/MM8108 802.11ah (Wi-Fi HaLow)
transceivers, modelled on the structure of
[cyw43-driver](https://github.com/georgerobotics/cyw43-driver): the driver is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest removing all references to cyw43-driver. the fact it's based on that is just to get a good head-start, it's not something that needs to stay in the docs/code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread src/mm_halow_config.h Outdated
// mm_halow_ticks_ms() -> uint32_t
// A free-running millisecond tick counter.
#ifndef mm_halow_ticks_ms
#error "port must define mm_halow_ticks_ms"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a macro. It could instead be just declared here as:

uint32_t mm_halow_ticks_ms(void);

and then a port can define it as inline:

static inline uint32_t mm_halow_ticks_ms(void) {
    return ...
}

The benefit of that way is it gives a clear signature for the function, and prevents use of macros which can lead to surprising outcomes.

(This should work fine, you can define a static inline function (done in mm_halow_configport.h prior to defining it as non-static (but not the other way around).)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DOne.

Comment thread src/mm_halow_config.h Outdated
// Pin accessors, applied to the MM_HALOW_CS/RESET/WAKE/BUSY/IRQ pin values
// defined by the port.
#ifndef mm_halow_hal_pin_write
#error "port must define the mm_halow_hal_pin_read/write/input/output accessors"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, suggest defining these as proper C function declarations instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/mm_halow_config.h Outdated

// mm_halow_in_irq() -> bool
// True when executing in interrupt context (on Cortex-M: IPSR != 0).
#ifndef mm_halow_in_irq

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, a proper C declaration.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/mm_halow_config.h
#endif

/*******************************************************************************/
// Functions the port must implement (see README.md).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could arguably be declared in a separate header eg mm_halow_port.h. But keeping them here (for now) is a reasonable pragmatic solution.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

Comment thread src/mm_halow_config.h Outdated
// Called when the driver hits an unrecoverable internal failure (a morselib
// assertion). The port routes this to its own assert/fatal-error handling; it
// must not return.
void mm_halow_assert_fail(void);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest mm_halow_port_assert_fail() to match other naming here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

A portable driver for the Morse Micro MM6108/MM8108 802.11ah (Wi-Fi
HaLow) transceivers, extracted from the MicroPython network.HALOW pull
request and reorganised along the lines of cyw43-driver:

- src/ holds the driver: the SD-over-SPI transport to the transceiver
  mapped onto a small mm_halow_port_* interface, an lwIP netif, a
  packet-memory pool, and an OSAL/scheduler shim that runs the vendor
  library (morselib) cooperatively off the host's scheduler with no
  vendor RTOS.
- mm_halow_config.h defines the host integration contract; the embedder
  provides mm_halow_configport.h (or MM_HALOW_CONFIG_FILE) with atomic
  sections, a millisecond tick, pin accessors, interrupt-context
  detection, and the port functions (SPI, private heap backing,
  hardware RNG, fallback MAC, optional edge IRQ).
- lib/mm-iot-sdk is the Morse Micro MM-IoT-SDK submodule, providing the
  prebuilt morselib and the transceiver firmware/BCF blobs; mm_halow.mk
  is a build fragment for make-based embedders covering morselib's
  newlib linkage, the blob embedding, and fetching the mm-iot-sdk
  submodule on demand.
- tests/host compiles the real allocator natively (233 checks, plus an
  ASan/UBSan variant); tests/qemu runs the real scheduler, whose
  context switch is naked asm, on a Cortex-M55 under QEMU (51 checks);
  tests/hardware carries the on-device throughput soak harness the
  driver was validated with.

Everything the driver needs from MicroPython in the original pull
request now arrives through the configuration contract: MICROPY_*
macros became MM_HALOW_*, the halow_* API became mm_halow_*, and the
machine-SPI, GC-heap and pin-interrupt couplings became port hooks.

Tested: both test suites pass locally (host: gcc -m32 + ASan; qemu:
arm-none-eabi-gcc 14.3.1 on mps3-an547).

Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>
Extends tools/ci.sh (added with the formatting and spelling CI) with the
test entry points, and wires them up:

- tests.yml runs the host allocator tests (plus ASan) and the QEMU
  Cortex-M55 scheduler test.
- tests_clang.yml builds and runs both with clang too; the QEMU test
  cross-compiles the sources with clang and links with GCC, so clang
  also covers mm_halow_sched.c, which the host tests do not build.

Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>

@dpgeorge dpgeorge left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, let's get this in.

@dpgeorge
dpgeorge merged commit 00d26f6 into micropython:main Sep 15, 2026
6 checks passed
@kwagyeman
kwagyeman deleted the add-driver branch September 15, 2026 23:44
kwagyeman added a commit to kwagyeman/micropython that referenced this pull request Sep 15, 2026
micropython/mm-halow-driver#1 has merged, so re-pin the submodule to the
merged commit and follow the driver's reviewed integration contract:

- The tunable options were renamed: MM_HALOW_AP -> MM_HALOW_ENABLE_AP,
  MM_HALOW_PIN_IRQ -> MM_HALOW_ENABLE_PIN_IRQ, MM_HALOW_CHIP ->
  MM_HALOW_CHIPSET, MM_HALOW_MAX_TASKS -> MM_HALOW_SCHED_MAX_TASKS.
- The tick, interrupt-context and pin hooks are now C function
  declarations rather than macros, so the configport provides them as
  static inline functions and defines mm_halow_pin_t.
- The driver's assert hook is mm_halow_port_assert_fail(), provided here.
- The driver no longer ships newlib syscall stubs (mm_halow_libc.c was
  removed), so the port provides the weak back-end morselib links
  against, and extmod.mk drops mm_halow_libc.c from the source list.

Builds green for OPENMV_N6 and OPENMV_AE3 (M55_DUAL).

Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants