Skip to content

baochip: New port for the Baochip-1x RISC-V SoC (DABAO board).#47

Draft
andrewleech wants to merge 201 commits into
review/baochip-portfrom
dabao
Draft

baochip: New port for the Baochip-1x RISC-V SoC (DABAO board).#47
andrewleech wants to merge 201 commits into
review/baochip-portfrom
dabao

Conversation

@andrewleech

Copy link
Copy Markdown
Owner

Summary

New port for the Baochip-1x, a VexRiscv RISC-V SoC with PULP UDMA peripherals, 4 MiB on-chip RRAM, 2 MiB SRAM, and a native USB controller exposed through the vendor boot1 bootloader. First (and currently only) supported board is the Crowd Supply Dabao evaluation board.

What works now: UART2 REPL at 115200 8N1, machine.Pin for all 96 GPIOs with IN/OUT/OPEN_DRAIN, machine.bootloader() to drop back into the vendor boot1 over USB-CDC, and machine.freq() returning the 350 MHz ACLK. Native USB-CDC, filesystem, and the rest of the peripheral drivers are still to come.

Uses dabao-sdk (Apache-2.0), a new open-source C SDK for this chip, separate from the Rust-based pattern that's also in development. Pinned at lib/dabao-sdk as a submodule. Boot signing uses the SDK's developer Ed25519ph key, so the resulting UF2 has a real signature but no meaningful attestation; the CI workflow installs pure25519 for this.

REPL UART RX is interrupt-driven, copying from the hardware 4-byte FIFO into the standard stdin ringbuf.

Open-drain Pin mode is software-emulated. The chip's GPIO block has no OD bit, so OD pins keep the DATA latch at 0 permanently and toggle direction to switch between drive-low (output) and float (input, relying on the external pull-up). A per-port mask tracks which pins are in OD mode so pin.value(v) knows which path to take; the mask is cleared on every soft reset.

A few things are deliberately stubbed with NotImplementedError and TODO markers: unique_id, set_freq, lightsleep with a timeout argument, and deepsleep. reset_cause() returns 0 until the SCG register mapping is done. MICROPY_CONFIG_ROM_LEVEL is set to EXTRA_FEATURES and the default boards/manifest.py includes asyncio.

Testing

Built and tested on the Dabao via the vendor's boot1 USB-CDC flashing path. Smoke tests cover REPL responsiveness, Pin toggle on PB1 (board pin -> CPU pin PA1), machine.bootloader() actually entering boot1, machine.freq() returning 350000000, time.ticks_ms() accuracy on a 100 ms sleep, and a 166-byte single-write burst into UART2 to verify the RX_CHAR path doesn't drop bytes.

No other ports touched.

Trade-offs and Alternatives

Software open-drain costs a small per-port mask in RAM and an extra branch in pin.value(), but the alternative would be to refuse OD mode entirely on this chip.

machine.bootloader() drives PC13 LOW for 1 ms before triggering the software reset. PC13 is dual-use on Dabao: the boot strap AND the 0.85 V regulator feedback path. Holding it LOW for longer interferes with the regulator, so the brief pulse is deliberate.

Generative AI

I used generative AI tools when creating this PR, but a human has checked the code and is responsible for the description above.

dpgeorge and others added 30 commits May 18, 2026 14:14
At version 5.9.0, to replace the existing copy at `lib/cmsis`.

Signed-off-by: Damien George <damien@micropython.org>
At version v6.3.0.

Signed-off-by: Damien George <damien@micropython.org>
This is a no-op change to the firmware because these CMSIS sources are
equivalent (verified building the default board for all 6 affected ports).

Signed-off-by: Damien George <damien@micropython.org>
This is now replaced by the `lib/CMSIS_5` submodule, which has exactly the
same content at the 5.9.0 tag in the directory `CMSIS/Core/Include`.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Built all 14 boards, all have binary equivalent firmware.

Signed-off-by: Damien George <damien@micropython.org>
Built all 8 PCA100xx boards, and they have binary equivalent firmware with
this change.

Signed-off-by: Damien George <damien@micropython.org>
Built all 18 boards, all have binary equivalent firmware.

Signed-off-by: Damien George <damien@micropython.org>
Built the 30 NUCLEO_xxx boards, all have binary equivalent firmware except
for NUCLEO_N657X0 (although it's firmware is the same size before and after
this commit).  Ran full test suite on OPENMV_N6 with CMSIS 6 to verify the
N6 and it passed.

Built mboot for NUCLEO_WB55, PYBV10, PYBD_SF2 and PYBD_SF6.  All have
binary equivalent firmware.

Signed-off-by: Damien George <damien@micropython.org>
This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Issue turned out to be something else, but I think there's still a
potential race here without this fix.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Was causing the next test to fail (maybe only when the other instance
skipped, not sure.)

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Test always fails on this chip, looks like possibly an ESP-IDF v5.5.1 bug.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Feature check .py.exp files are not needed at all (and most of them are
empty).  Instead, the output of the feaure test is checked against a known
value within `run-tests.py` to see if that feature is enabled or not (and
in some cases like `byteorder.py` there are multiple valid output
values).

Signed-off-by: Damien George <damien@micropython.org>
They are provided in a private header that is already included.

Signed-off-by: Damien George <damien@micropython.org>
Also support v5.5.4, but don't use it yet due to possible issues discussed
in micropython#19149.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Instead of unconditionally (ie when the `select` module is enabled).  This
way, a minimal board can enable the `select` module (eg for asyncio) but
still have `select.select()` disabled by default to save size.

Signed-off-by: Damien George <damien@micropython.org>
Prior to this change some of the smaller boards (eg nRF51) would have
the .py asyncio code frozen but be missing both the `select` module and
the core C-level `_asyncio` module, making `asyncio` useless.

This commit lets asyncio be configured at the makefile level, in order to
freeze the correct code.  Both the `select` and `_asyncio` modules will be
enabled if asyncio is enabled.

asyncio is now enabled by default on all boards except MICROBIT (which is
too small due to the other modules is has).  This costs about +2100 bytes
on boards that did not already have it enabled, eg PCA10028.

Signed-off-by: Damien George <damien@micropython.org>
To match other ports, and allow the `pyproject.toml` F821 ignore rule to
cover it.

Signed-off-by: Damien George <damien@micropython.org>
The UART DRE flag is always set if the DATA register is empty.  That would
lead to a TXIDLE event each time the IRQ handler was run.  To fix that,
only fire the TXIDLE event if the DRE interrupt flag is enabled.

Partially fixes the `tests/extmod/machine_uart_irq_txidle.py` test on
`ADAFRUIT_ITSYBITSY_M0_EXPRESS`.

Signed-off-by: Damien George <damien@micropython.org>
The C IRQ handler can be called with multiple IRQ flags active, eg both
RXC and DRE set.  But the Python IRQ handler should only see the events
that it registered.  So mask the flags as appropriate.

Combined with the parent commit, `tests/extmod/machine_uart_irq_txidle.py`
now passes on `ADAFRUIT_ITSYBITSY_M0_EXPRESS`.

Signed-off-by: Damien George <damien@micropython.org>
Clear TRCENA (if it was enabled for DWT CYCCNT) before
calling NVIC_SystemReset(), otherwise it spins forever.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
Signed-off-by: Damien George <damien@micropython.org>
The LPTIMER does not reset when the CPU is reset and may have state left
over, eg a timer interrupt running.  So clear that when the device starts
up.

This fixes `machine.deepsleep()` in the follow scenario: the device first
wakes via LPTIMER but then goes to deepsleep again without it, and is
unable to wake again via LPGPIO.

Signed-off-by: Damien George <damien@micropython.org>
This allows LPTIMER to wake the device from lightsleep mode.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Passing in a millisecond timeout argument to `machine.lightslep()` will
configure the LPTIMER to wake the system.

Signed-off-by: Damien George <damien@micropython.org>
This allows the GPIO IRQ to be enabled even if a Python `Pin.irq()` is not
configured for that pin.

Signed-off-by: Damien George <damien@micropython.org>
projectgus and others added 29 commits June 25, 2026 23:30
This seems like it's only really a problem on Debug builds, but
I think can't hurt to increase it on all windows builds.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This removes the last usage of mp_binary_set_val_array_from_int(). It
will be a little slower, but shouldn't be measurably so compared to the
ADC sampling.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
If >1 unittest-enabled module is included, the results of the merged
module won't match (as it runs some previously registered tests again).

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This PR was originally a cherry-pick of CircuitPython commits 095c844,
d103ac1, c592bd6 and 8664a65.

However, substantial additional changes so the implementation has diverged
a lot from CircuitPython's:

- Keep CPython >= 3.11 defaults for int.to_bytes(), consistent
  with 80c5e76 and 0b432b3.

- Refactors to reduce the code size impact of this change. Various code
  paths are now funneled into new function mp_obj_int_to_bytes() and
  existing function mp_binary_set_int(), except where a simple assignment
  is used for performance reasons (i.e. array, moductypes).

- Keep the MicroPython behaviour of not overflow checking assignments to
  arrays, bytearrays, etc. - but enable overflow checks as part of
  MicroPython V2.0.

- Update tests to work with the new behaviour (similar to CPython) and add
  coverage for new code and some corner cases. Some tests are converted to
  unittest so we can easily verify both current and V2.0 overflow.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Generated `qstr.i.last` files (output from the "pp" phase) can be very
large due to inclusion of large HAL headers.  Yet only a small amount of
this data is actually needed by the "split" phase of `makeqstrdefs.py`.

This commit improves the situation by filtering out unneeded lines as early
as possible: in the "pp" phase the C preprocessor runs, its output is
parsed via a pipe connection, and only those lines that are needed are
written out to the `qstr.i.last` file.  When the "split" phase runs it then
sees only the necessary lines (it does further regex filtering to get
exactly the lines it needs for the given mode).

Windows already does this filtering (it does not use `makeqstrdefs.py` for
the preprocessor phase) in its `ConcatPreProcFiles` function, and that
was added long ago in 29c8c8a.

This change significantly reduces the output build size and improves the
build speed:

- stm32 BOARD=PYBV10: build output goes from 152M down to 32M, and build is
  10% faster (29s down to 26s).  Building all stm32 boards, the total build
  size goes from about 11GB down to about 2.5GB.

- rp2 BOARD=RPI_PICO: build output goes from 87M down to 39M, and build is
  about 5% faster (23s down to 21.6s).

- esp32 BOARD=ESP32_GENERIC: build output goes from 307M down to 230M, and
  build is about 6% faster (101s down to 95s).

Tested all three ports above, that the files in build/genhdr are equivalent
(except of course `qstr.i.last`), and that PYBV10 is binary equivalent
before and after this change.

Signed-off-by: Damien George <damien@micropython.org>
This adds a new `--trace-output` option to `run-tests.py` which mimics the
same option already in `run-multitests.py`.  Using it, the output from the
test running on a board is printed to stdout as it is received.

This allows easier debugging of tests, to see the output in real time.  For
tests that have complicated run parameters, eg using via-mpy or needing a
target wiring script, running them standalone (eg with `mpremote`) is not
possible, so using `run-tests.py` becomes mandatory, hence the need to
easily see the output without having to view the result file separately at
the end.  It's also useful to watch tests that pass but are long running.

Signed-off-by: Damien George <damien@micropython.org>
Similar to other classes in the `machine` module, `SDCard` is now there
for a port to use if it enables MICROPY_PY_MACHINE_SDCARD.

Signed-off-by: Damien George <damien@micropython.org>
This is now provided by the common one in `extmod/modmachine.c`.

Signed-off-by: Damien George <damien@micropython.org>
To unify this with other ports, and to enable the common `machine.SDCard`
entry in `extmod/modmachine.c`.

Signed-off-by: Damien George <damien@micropython.org>
To unify this with other ports, and to enable the common `machine.SDCard`
entry in `extmod/modmachine.c`.

Signed-off-by: Damien George <damien@micropython.org>
This aligns the stm32 port with other ports that have `machine.SDCard`.
The same `SDCard` class is used for this and the existing `pyb.SDCard`, and
its API matches the standard one.

Signed-off-by: Damien George <damien@micropython.org>
Add a minimal `network.WLAN` CSI API for ESP32 builds with CSI
enabled. Also enable ESP-IDF CSI support in the standard generic
`ESP32`, `ESP32-C3`, `ESP32-S3`, `ESP32-C5`, and `ESP32-C6`
board definitions.

Signed-off-by: Francesco Pace <francesco.pace@gmail.com>
Add a board definition for the Seeed Studio XIAO ESP32C3
(ESP32-C3 with 4MB flash).
The REPL is on the native USB Serial/JTAG interface,
leaving UART0 free for the application.

Signed-off-by: Pavel Revak <pavelrevak@gmail.com>
Add a board definition for the Seeed Studio XIAO ESP32C5
(ESP32-C5 with 8MB flash and 8MB quad SPIRAM).
The REPL is on the native USB Serial/JTAG interface,
leaving UART0 free for the application.

Signed-off-by: Pavel Revak <pavelrevak@gmail.com>
Add a board definition for the Seeed Studio XIAO ESP32S3
(ESP32-S3 with 8MB flash and 8MB octal SPIRAM).
The REPL is on the native USB interface, leaving UART0 free
for the application.

The same firmware also runs on the XIAO ESP32S3 Plus: the flash size is
auto-detected, and the Plus's extra pins (D11-D19) are included in
pins.csv, which does not affect the standard board.

Signed-off-by: Pavel Revak <pavelrevak@gmail.com>
By default the new I2C driver is enabled for esp-idf version >= 5.5.2.

With v5.5.2 and up the the additional bus probing is not needed any
more. It was needed because transfers to a non-existing address caused
an error message to be printed instead of just returning an error code.
With esp-idf v5.5.2 it returns just an error code.
For 0-length writes as being used by i2c.scan(), an additional code path
using i2c_master_execute_defined_operations() is needed to avoid an
error message.

Tested with a Generic ESP32 and esp-idf v5.5.1, v5.5.2 and v5.5.4.
Tested as well using the new driver with v5.5.1.
Tested V5.5.2 with Generic ESP32, ESP32S2, ESP32S3, ESP32C2, ESP32C3,
ESP32C5, ESP32C6, ESP32P4. Driving an external device as I2C controller.

There is a slight inconsistency: reading 0 bytes from a non-existing
address return an empty bytes object and does not raise an error.
Writing 0 bytes to a non-existing address returns an error. That case
is used by i2c.scan().

Signed-off-by: robert-hh <robert@hammelrath.com>
If the new driver is enabled for versions previous to v5.5.2, an error
is raised and compilation aborted.

Signed-off-by: robert-hh <robert@hammelrath.com>
The `MP_SEEK_xxx` constants are defined in `py/stream.h` exactly for use
by `py/stream.c` so that it doesn't depend on the C library.

Signed-off-by: Damien George <damien@micropython.org>
Instead of magic numbers + a comment.

Signed-off-by: Damien George <damien@micropython.org>
They are no longer needed.

Signed-off-by: Damien George <damien@micropython.org>
This helper function is used only by the C stack control functions (in
`py/cstack.c` and `py/stackctrl.c`) and not by pystack.  Similarly the qstr
message is only used by this `mp_raise_recursion_depth()` function.

(`mp_raise_recursion_depth()` is also used by the VM in strict stacless
mode when pystack is disabled, but for that configuration one should anyway
be enabling C stack checking.)

Signed-off-by: Damien George <damien@micropython.org>
This variable does nothing in mpy-cross because:
- `MICROPY_DEBUG_PRINTERS` is not enabled, so the relevant code in
  `py/compile.c` that uses `mp_verbose_flag` is unused.
- Even if `MICROPY_DEBUG_PRINTERS` was enabled, nothing happens because
  mpy-cross has `MP_PLAT_PRINT_STRN` defined to do nothing.
- `MICROPY_PY_MICROPYTHON_MEM_INFO` is not enabled.

So remove it.  But keep the `-v` CLI option to not break existing uses of
mpy-cross, and keep it compatible with the CLI options of the unix port.

Signed-off-by: Damien George <damien@micropython.org>
This stray global variable doesn't really belong in `py/emitglue.c` because
it's currently only used by `py/compile.c`.  So move it to the state
context struct alongside the related compiler settings.

Signed-off-by: Damien George <damien@micropython.org>
Adds the ports/baochip directory with Makefile, linker script,
mpconfigport.h, and the DABAO board's mpconfigboard.h.  The port
uses the dabao-sdk submodule (Apache-2.0) as its vendor layer,
selecting specific SDK .c and .s files rather than linking a
prebuilt library.  SDK crt0 and the IRQ dispatcher are wired in.
MicroPython core boots and prints the banner via UART2.

Signed-off-by: Andrew Leech <andrew@alelec.net>
Wires mp_hal stdout and stdin through UART2 (115200 8N1).  UART RX
is driven by an IRQ-fed ring buffer; a direct UART_VALID poll is kept
as a fallback for contexts where the IRQ has not yet fired.  Soft-reset
restarts the REPL cleanly.  Ctrl-A enters raw REPL mode.

TickTimer is initialised with CLOCKS_PER_TICK = ACLK/1e6, giving a
1 us tick.  mp_hal_ticks_ms/us/cpu read the 64-bit free-running counter
with a race-safe high/low read; mp_hal_delay_ms/us busy-wait on the
same source.  Enables MICROPY_PY_TIME for time.sleep_ms, ticks_ms,
ticks_diff etc. from the REPL (time.time stays off until a RTC lands).

Signed-off-by: Andrew Leech <andrew@alelec.net>
Adds machine.Pin backed by ROM pin objects generated by boardgen
(boards/make-pins.py), one const struct per pin stored in flash.
The struct packs port+pin into two uint8_t fields and uses
qstr_short_t for the name, keeping each object at 8 bytes with no
padding (96 total pins = 768 bytes flash, no heap).

Pin.board and Pin.cpu sub-namespaces are backed by the generated
locals dict; string lookup searches board then cpu.  Modes IN, OUT,
and OPEN_DRAIN (emulated via direction toggling) are supported.
Pull-up is the only pull option (hardware limitation).

The SDK fork is updated to include gpio_get_dir(), which reads GPIOOE
to determine output-enable state, needed for Pin.__repr__.

Signed-off-by: Andrew Leech <andrew@alelec.net>
Drives PC13 (PROG strap, SW2) low for one reset cycle to signal
boot1, which holds the chip in its CDC flashing REPL until PROG is
released.  BOOTLOADER_PORT and BOOTLOADER_PIN are taken from
mpconfigboard.h so boards can override the strap pin.

Signed-off-by: Andrew Leech <andrew@alelec.net>
Adds board.json (upstream board catalog metadata), pins.csv (every
PB/PC pin on the 32-pin header plus functional aliases for UART2,
I2C0, SPI2, QSPI1, and the PROG strap), and deploy.md (documents
the two flash paths: machine.bootloader() from a running REPL, and
PROG+RESET when the board is in an unknown state).

Signed-off-by: Andrew Leech <andrew@alelec.net>
Adds ports_baochip.yml to build the DABAO board on every push and
PR.  Wires the baochip build into ci.sh alongside the other ports.

Signed-off-by: Andrew Leech <andrew@alelec.net>
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.