Skip to content

rp2: Add support for new RP2350 MCU. #15619

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Oct 15, 2024
Merged

Conversation

dpgeorge
Copy link
Member

@dpgeorge dpgeorge commented Aug 8, 2024

Summary

This PR adds support for the new RP2350 MCU, and the new Pico 2 board.

Both the Cortex-M33 and RISC-V CPUs are supported. You can build for them both (as separate firmware) with:

make BOARD=RPI_PICO2
make BOARD=RPI_PICO2 BOARD_VARIANT=RISCV

Download the .uf2 to the board as usual via mass storage. The bootloader will detect which architecture (ARM or RISC-V) the firmware has and boot the correct CPU(s).

See https://www.raspberrypi.com/products/rp2350/

Thanks to @peterharperuk and @Gadgetoid for doing a lot of the work in this PR.

Testing

The existing test suite (mostly) passes on the Pico 2, in both ARM and RISC-V mode. There are some issues with lightsleep mode on ARM and machine-idle on both ARM and RISC-V which need to be sorted out.

TODO

  • fix lightsleep will do at a later date when there's a new version of pico-sdk with required improvements
  • get all tests passing with RPI_PICO2 and RPI_PICO2-RISCV firmware
  • work out why RPI_PICO_W build has increased so much -> because of time zone support in libc, fixed by providing custom localtime_r and mktime
  • test RPI_PICO and RPI_PICO_W
  • test other TinyUSB ports no longer needed, TinyUSB already updated in lib/tinyusb: Update TinyUSB to release 0.17.0 #15902
  • decide on board name: RPI_PICO2 vs RPI_PICO_2 (latter matches RPI_PICO_W, and officially it's Pico 2) -> go with RPI_PICO2 to match pico-sdk header pico2.h

Copy link

github-actions bot commented Aug 8, 2024

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:  -108 -0.012% RPI_PICO_W[incl +68(bss)]
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@Gadgetoid Gadgetoid mentioned this pull request Aug 8, 2024
4 tasks
Copy link

codecov bot commented Aug 8, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.57%. Comparing base (fa942d5) to head (51663b9).
Report is 31 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #15619   +/-   ##
=======================================
  Coverage   98.57%   98.57%           
=======================================
  Files         164      164           
  Lines       21336    21336           
=======================================
  Hits        21031    21031           
  Misses        305      305           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@dpgeorge
Copy link
Member Author

dpgeorge commented Aug 8, 2024

Pre-built firmware can be found here: https://micropython.org/download/RPI_PICO2/

@dpgeorge dpgeorge force-pushed the rp2-add-rp2350 branch 2 times, most recently from e20b042 to eed9e22 Compare August 9, 2024 01:26
@Gadgetoid
Copy link
Contributor

work out why RPI_PICO_W build has increased so much

Build size, or build time? Do we need to set -DPICO_NO_COPRO_DIS=1?

@dpgeorge
Copy link
Member Author

Build size, or build time? Do we need to set -DPICO_NO_COPRO_DIS=1?

Build/firmware size.

Setting DPICO_NO_COPRO_DIS=1 does not seem to change anything (on RP2040 builds at least).

@dpgeorge
Copy link
Member Author

work out why RPI_PICO_W build has increased so much

It looks like this is due to the new always-on timer (aon timer) code, which calls localtime_r(), which subsequently pulls in timezone support and calls to large printf and scanf functions (from the C stdlib, newlib).

It doesn't look easy to remove all that code even though it's probably never used by MicroPython. So we may have to live with it.

@Gadgetoid
Copy link
Contributor

Gadgetoid commented Aug 12, 2024

Hmm so aon_timer_set_time() calls time_to_datetime() from pico_util.

It might be possible to provide our own pico_util with a more MicroPython-friendly alternative for that function, or just to shim localtime_r, eg:

target_link_options(${MICROPY_TARGET} PRIVATE "LINKER:--wrap=localtime_r")
struct tm* __wrap_localtime_r(time_t *time, struct tm *local_time) {
    return gmtime_r(time, local_time);
}

Which I think resolves to mbedtls_platform_gmtime_r

Edit: See: dpgeorge/micropython@rp2-add-rp2350...pimoroni:micropython:patch-localtime_r

@dpgeorge
Copy link
Member Author

It might be possible to provide our own pico_util with a more MicroPython-friendly alternative for that function, or just to shim localtime_r, eg:

Good idea.... but I tried your patch and it did not work, it only reduced firmware size by about 1k. We need to reclaim 17k.

Looking at firmware.elf for RPI_PICO, it still pulls in iswspace (for example, wide character handling), which is needed by siscanf, which is needed by _tzset_unlocked, which is needed by mktime, which is needed by datetime_to_time, which is needed by aon_timer_get_time.

@kilograham
Copy link
Contributor

Ah, yes, this might be a vote against using the aon_timer API on RP2040 after all - the POWMAN timer is not date based, but the RTC one is, so one of them takes the hit for a common API :-( ... we should probably add both forms for both though (and maybe a documentation warning)

@projectgus
Copy link
Contributor

Here's an extended version of @Gadgetoid's patch that gets the code size all the way down (PICO_W binary size is actually slightly smaller than the current master). I have verified this compiles and boots but I have not tested it further:
projectgus@debc0ad

(Two tips for anyone who is interested: 1. Linker wrap is actually not necessary for any symbol that is linked from a library, as any object provided on the linker command line will take precedence. Great blog post on the details. 2. The --cref linker option is worth its weight in gold for figuring this stuff out. I will submit a PR to add it either to pico-sdk or rp2 port linker command lines.)

Copy link
Contributor

@projectgus projectgus left a comment

Choose a reason for hiding this comment

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

This is an impressively small and clean set of changes for adding not just a new chip but also a new CPU arch! Kudos to the pico-sdk team and everyone who worked on this PR.

@Gadgetoid
Copy link
Contributor

Here's an extended version of @Gadgetoid's patch that gets the code size all the way down

I've said it once, I'll say it again, Open Source is just the formal version of posting the wrong answer on the internet to get the right one 😆 thank you!

I've just run our batteries-included builds against this branch and that extra 17k blows our flash budget 😬

@ZodiusInfuser
Copy link
Contributor

Hey. I just wanted to chime in here to point out that one of our PGA2350 users (which uses the B chip) was not able to use PWM on the pins above 31. Checking this repo, it seems machine_pwm has no additions for the extra 8 channels yet.

@Gadgetoid
Copy link
Contributor

Gadgetoid commented Aug 15, 2024

Afacit PWM just needs this: pimoroni@7f1fcc7

I'll cut some builds to test it!

PGA builds here should include the fix: https://github.com/pimoroni/pga/actions/runs/10391381219
Plus PPP2 here: https://github.com/pimoroni/pimoroni-pico-rp2350/actions/runs/10385135105

Edit 2: Afaict this fix works. @dpgeorge if you don't mind casting your eye over it and cherry-picking into your RP2350 feature branch, I can rebase accordingly. Thank you!

@ZodiusInfuser
Copy link
Contributor

Thanks @Gadgetoid . Looks like there's at least one hardcoded slice count left:

void machine_pwm_deinit_all(void) {
    for (int i = 0; i < 8; i++) {
        slice_freq_set[i] = false;
        pwm_set_enabled(machine_pwm_obj[i].slice, false);
    }
}

@Gadgetoid
Copy link
Contributor

Gadgetoid commented Aug 15, 2024

Good spot! Curse the magic numbers! Pushed a fix.

Edit: Updated the link above, but for clarity the patch is now: pimoroni@7f1fcc7

Our very own Hodgy has a PGA2350 set up with all of the PWM (it looks like an electronic mangrove) and confirmed this works.

@mattytrentini
Copy link
Contributor

Built RPI_PICO2 and deployed it to the Seeed XIAO RP2350; it boots and at least some basic operations appear to be working fine! 😄

> mpremote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.24.0-preview.203.gb9f6698d30.dirty on 2024-08-23; Raspberry Pi Pico2 with RP2350
Type "help()" for more information.
>>> from machine import Pin
>>> # Check user LED
>>> led = Pin(25, Pin.OUT)
>>> led.toggle()
>>> # Check RGB LED
>>> power_pin = Pin(23, Pin.OUT) # Provide power first
>>> power_pin.value(1)
>>> from neopixel import NeoPixel
>>> neo = NeoPixel(Pin(22), 1)
>>> neo[0] = (0, 0, 40)
>>> neo.write() # Blue LED

@dpgeorge
Copy link
Member Author

Here's an extended version of @Gadgetoid's patch that gets the code size all the way down (PICO_W binary size is actually slightly smaller than the current master).

Thanks for that, I've now added that commit to this PR.

@dpgeorge
Copy link
Member Author

decide on board name: RPI_PICO2 vs RPI_PICO_2 (latter matches RPI_PICO_W, and officially it's Pico 2)

The decision is to stick with RPI_PICO2, and then also RPI_PICO2_W (eventually), because that matches the pico-sdk header file names: pico.h, pico_w.h, pico2.h and pico2_w.h

@Gadgetoid
Copy link
Contributor

Looks like I have some PSRAM rebasing to do! Will sure make it easier to play with the new UART/PPP stuff 😁

(I know you’re probably still working your way down here, but don’t miss the PWM patch above! - pimoroni@7f1fcc7 )

@Gadgetoid
Copy link
Contributor

Gadgetoid commented Aug 30, 2024

Might be my board config, but I run into this when trying to build for RP2350B or PICO_NUM_GPIOS=48-

/home/runner/work/pimoroni-pico-rp2350/pimoroni-pico-rp2350/micropython/ports/rp2/rp2_pio.c: In function 'rp2_pio_gpio_base':
/home/runner/work/pimoroni-pico-rp2350/pimoroni-pico-rp2350/micropython/ports/rp2/rp2_pio.c:411:48: error: 'pin_GPIO0' undeclared (first use in this function)
  411 |     return pio_get_gpio_base(self->pio) == 0 ? pin_GPIO0 : pin_GPIO16;
      |                                                ^~~~~~~~~

Edit: Might need to include pins.h for these?

Edit2: Looks like nrf uses #include "genhdr/pins.h" but doing this on RP2 causes:

genhdr/pins.h:2:21: error: 'machine_pin_obj_table' undeclared (first use in this function)
    2 | #define pin_GPIO0 (&machine_pin_obj_table[0])
      |                     ^~~~~~~~~~~~~~~~~~~~~

Since this table is defined in <BOARD_NAME>_pins.c (included in the CMakeLists) but not declared anywhere. machine_pin.c kinda fudges around this by including extern const machine_pin_obj_t machine_pin_obj_table[NUM_BANK0_GPIOS]; but that doesn't make pins accessible elsewhere...

Oh also:

micropython/ports/rp2/rp2_pio.c: In function 'rp2_pio_gpio_base':
micropython/ports/rp2/rp2_pio.c:415:58: error: return discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]
  415 |     return pio_get_gpio_base(self->pio) == 0 ? pin_GPIO0 : pin_GPIO16;

The following heinous patch gets things building for me again:

diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 5e69da4b8..9dc5dbc94 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -538,6 +538,12 @@ set_source_files_properties(
     COMPILE_OPTIONS "-O2"
 )
 
+set_source_files_properties(
+    rp2_pio.c
+    PROPERTIES
+    COMPILE_OPTIONS "-Wno-error=discarded-qualifiers"
+)
+
 set_source_files_properties(
     ${PICO_SDK_PATH}/src/rp2_common/pico_double/double_math.c
     ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_math.c
diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c
index 8e91c1d8f..0d8854715 100644
--- a/ports/rp2/rp2_pio.c
+++ b/ports/rp2/rp2_pio.c
@@ -32,11 +32,15 @@
 #include "py/mphal.h"
 #include "shared/runtime/mpirq.h"
 #include "modrp2.h"
+#include "machine_pin.h"
+#include "genhdr/pins.h"
 
 #include "hardware/clocks.h"
 #include "hardware/irq.h"
 #include "hardware/pio.h"
 
+extern const machine_pin_obj_t machine_pin_obj_table[NUM_BANK0_GPIOS];
+
 typedef struct _rp2_pio_obj_t {
     mp_obj_base_t base;
     PIO pio;

@dpgeorge dpgeorge added this to the release-1.24.0 milestone Aug 30, 2024
peterharperuk and others added 25 commits October 15, 2024 12:07
This commit separates various build settings and include files that are
specific to RP2040 and RP2350, and uses the aon_timer interface instead of
rtc, to work across both MCU variants.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Phil Howard <phil@gadgetoid.com>
Update NUM_GPIOS to match NUM_BANK0_GPIOS, and increase bit-width of
variables that store pin numbers.

Signed-off-by: Phil Howard <phil@gadgetoid.com>
So other code can include `machine_pin.h` and use the pin name macros such
as `pin_GPIO0`.

Signed-off-by: Damien George <damien@micropython.org>
Add support for 32 and 48 pin variants of RP2350.

Add new `PIO.gpio_base()` method, mirroring the Pico SDK.

Signed-off-by: Phil Howard <phil@gadgetoid.com>
Signed-off-by: Damien George <damien@micropython.org>
NUM_GPIOS amd NUM_EXT_GPIOS are currently hardcoded in make-pins.py, which
makes it difficult to support SoCs with different pin count.

This commit generalises make-pins.py by passing in the pin count in via the
new arguments `--num-gpios` and `--num-ext-gpios`.  These default to the
current values supported by Pico, namely 30/10.  This can be changed with
PICO_NUM_GPIOS and PICO_NUM_EXT_GPIOS in `mpconfigboard.cmake`.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Fix the gpio_irq function so that it looks at all six iobank0_hw->intr[n]
registers, for up to 48 IOs.

Signed-off-by: Phil Howard <phil@gadgetoid.com>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
As part of this change, the RV32I native emitter is enabled on RISCV
board variants.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
In case it doesn't have the correct value.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Provide stub implementations of localtime_r() and mktime() to avoid
code size increase.

Reported upstream at raspberrypi/pico-sdk#1810

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Signed-off-by: Phil Howard <phil@gadgetoid.com>
Signed-off-by: Damien George <damien@micropython.org>
This is the same form-factor as the Pico but with an RP2350.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
To test construction of UART instances.

Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
Otherwise a previous value of `timeout_char` may be left over after a soft
reset.

Signed-off-by: Damien George <damien@micropython.org>
@dpgeorge dpgeorge merged commit 51663b9 into micropython:master Oct 15, 2024
67 checks passed
@dpgeorge dpgeorge deleted the rp2-add-rp2350 branch October 15, 2024 02:02
@dpgeorge
Copy link
Member Author

This is finally merged! Thanks to everyone here who contributed/helped/tested/reviewed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.