diff --git a/.github/workflows/pico-build.yaml b/.github/workflows/pico-build.yaml index 14ee61135..f317aeaa0 100644 --- a/.github/workflows/pico-build.yaml +++ b/.github/workflows/pico-build.yaml @@ -29,6 +29,10 @@ concurrency: jobs: pico: runs-on: ubuntu-latest + strategy: + matrix: + board: ["pico", "pico_w"] + steps: - name: Checkout repo uses: actions/checkout@v3 @@ -43,7 +47,7 @@ jobs: set -euo pipefail mkdir build cd build - cmake .. -G Ninja + cmake .. -G Ninja -DPICO_BOARD=${{ matrix.board }} ninja - name: Install nvm and nodejs 20 @@ -56,6 +60,8 @@ jobs: - name: Run tests with rp2040js shell: bash working-directory: ./src/platforms/rp2040/tests + # Unfortunately, rp2040js doesn't support cyw43 and cyw43_arch_init panics + if: success() && matrix.board != "pico_w" run: | set -euo pipefail source $HOME/.nvm/nvm.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 4234644be..8d0bd3e5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `crypto:crypto_one_time/4,5` on ESP32 - Improved nif and port support on STM32 - Added support for `atomvm:posix_clock_settime/2` +- Added support initial for Pico-W with the on-board LED ### Fixed @@ -30,6 +31,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug in the STM32 port that caused the final result to never be returned. - Fix bug when building a binary using a 64-bit integer on a 32-bit CPU. +### Breaking changes +- Pico applications now need to be located at 0x10100000 (instead of 0x100A0000). + Users need to update the parameters passed to uf2tool. + ## [0.6.0-alpha.0] - 2023-08-13 ### Added diff --git a/CMakeModules/BuildErlang.cmake b/CMakeModules/BuildErlang.cmake index 0995657bd..b4c97d380 100644 --- a/CMakeModules/BuildErlang.cmake +++ b/CMakeModules/BuildErlang.cmake @@ -85,7 +85,7 @@ macro(pack_lib avm_name) add_custom_target( ${avm_name}.uf2 ALL - COMMAND ${CMAKE_BINARY_DIR}/tools/uf2tool/uf2tool create -o ${avm_name}.uf2 -s 0x10080000 ${avm_name}.avm + COMMAND ${CMAKE_BINARY_DIR}/tools/uf2tool/uf2tool create -o ${avm_name}.uf2 -s 0x10100000 ${avm_name}.avm COMMENT "Creating UF2 file ${avm_name}.uf2" VERBATIM ) @@ -208,7 +208,7 @@ macro(pack_uf2 avm_name main) add_custom_target( ${avm_name}.uf2 ALL - COMMAND ${CMAKE_BINARY_DIR}/tools/uf2tool/uf2tool create -o ${avm_name}.uf2 -s 0x100A0000 ${avm_name}.avm + COMMAND ${CMAKE_BINARY_DIR}/tools/uf2tool/uf2tool create -o ${avm_name}.uf2 -s 0x10180000 ${avm_name}.avm COMMENT "Creating UF2 file ${avm_name}.uf2" VERBATIM ) diff --git a/README.PICO.Md b/README.PICO.Md index d7923b371..8aaa7a569 100644 --- a/README.PICO.Md +++ b/README.PICO.Md @@ -22,6 +22,20 @@ ninja You may want to build with option `AVM_REBOOT_ON_NOT_OK` so Pico restarts on error. + +Building AtomVM for Raspberry Pico-W +================================== + +To build for Pico-W, pass the board to cmake as follows: + +``` +cd src/platforms/rp2040/ +mkdir build +cd build +cmake .. -G Ninja -DPICO_BOARD=pico_w +ninja +``` + Installing AtomVM and programs on Raspberry Pico ================================================ @@ -89,10 +103,10 @@ You need to create an avm file using PackBEAM binary (or rebar3 plugin). ``` Then the BEAM file must be converted to UF2. -The VM currently expects the application to be loaded at address 0x100A0000. +The VM currently expects the application to be loaded at address 0x10100000. ```sh -./uf2tool create -o packed.uf2 -s 0x100A0000 packed.avm +./uf2tool create -o packed.uf2 -s 0x10100000 packed.avm ``` Copy this UF2 to the Pico after you copied the VM (`AtomVM.uf2`) and the diff --git a/doc/src/build-instructions.md b/doc/src/build-instructions.md index 9b80c612c..865780fea 100644 --- a/doc/src/build-instructions.md +++ b/doc/src/build-instructions.md @@ -560,7 +560,7 @@ is 8N1 with no flow control. * `Erlang/OTP` * `Elixir` (optional) -### AtomVM build steps +### AtomVM build steps (Pico) cd src/platforms/rp2040/ mkdir build @@ -570,6 +570,16 @@ is 8N1 with no flow control. > You may want to build with option `AVM_REBOOT_ON_NOT_OK` so Pico restarts on error. +### AtomVM build steps (Pico-W) + + cd src/platforms/rp2040/ + mkdir build + cd build + cmake .. -G Ninja -DPICO_BOARD=pico_w + ninja + +> You may want to build with option `AVM_REBOOT_ON_NOT_OK` so Pico restarts on error. + ### libAtomVM build steps Build of standard libraries is part of the generic unix build. diff --git a/doc/src/getting-started-guide.md b/doc/src/getting-started-guide.md index d7860e474..0918ec3d6 100644 --- a/doc/src/getting-started-guide.md +++ b/doc/src/getting-started-guide.md @@ -306,9 +306,9 @@ or rebar3 packbeam -p -i packed.avm module.beam Then the BEAM file must be converted to UF2. -The VM currently expects the application to be loaded at address 0x100A0000. +The VM currently expects the application to be loaded at address 0x10100000. - ./uf2tool create -o packed.uf2 -s 0x100A0000 packed.avm + ./uf2tool create -o packed.uf2 -s 0x10100000 packed.avm Copy this UF2 to the Pico after you copied the VM (`AtomVM.uf2`) and the standard libraries (`atomvmlib.uf2`). diff --git a/examples/erlang/rp2040/CMakeLists.txt b/examples/erlang/rp2040/CMakeLists.txt index 2af632824..10823b1e4 100644 --- a/examples/erlang/rp2040/CMakeLists.txt +++ b/examples/erlang/rp2040/CMakeLists.txt @@ -25,3 +25,4 @@ include(BuildErlang) pack_uf2(hello_pico hello_pico eavmlib estdlib) pack_uf2(pico_blink pico_blink eavmlib estdlib) pack_uf2(pico_rtc pico_rtc eavmlib estdlib) +pack_uf2(picow_blink picow_blink eavmlib estdlib) diff --git a/examples/erlang/rp2040/pico_blink.erl b/examples/erlang/rp2040/pico_blink.erl index cd0da9525..588ca2f51 100644 --- a/examples/erlang/rp2040/pico_blink.erl +++ b/examples/erlang/rp2040/pico_blink.erl @@ -21,7 +21,8 @@ -module(pico_blink). -export([start/0]). -% 25 is on-board led on Pico & Pico-W +% 25 is on-board led on Pico +% This code will not work on Pico-W where GPIO 25 has another purpose. -define(GPIO_NUM, 25). start() -> diff --git a/examples/erlang/rp2040/picow_blink.erl b/examples/erlang/rp2040/picow_blink.erl new file mode 100644 index 000000000..99ee6ad07 --- /dev/null +++ b/examples/erlang/rp2040/picow_blink.erl @@ -0,0 +1,34 @@ +% +% This file is part of AtomVM. +% +% Copyright 2023 Paul Guyot +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. +% +% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +% + +-module(picow_blink). +-export([start/0]). + +% 0 is CYW43 GPIO for on-board led +% This code will not work on Pico +-define(GPIO_NUM, 0). + +start() -> + loop(0). + +loop(N) -> + pico:cyw43_arch_gpio_put(?GPIO_NUM, N), + timer:sleep(1000), + loop((N + 1) rem 2). diff --git a/libs/eavmlib/src/pico.erl b/libs/eavmlib/src/pico.erl index 8f67b7f15..4b88c5542 100644 --- a/libs/eavmlib/src/pico.erl +++ b/libs/eavmlib/src/pico.erl @@ -27,7 +27,9 @@ -module(pico). -export([ - rtc_set_datetime/1 + rtc_set_datetime/1, + cyw43_arch_gpio_get/1, + cyw43_arch_gpio_put/2 ]). %%----------------------------------------------------------------------------- @@ -38,3 +40,27 @@ -spec rtc_set_datetime(calendar:datetime()) -> ok. rtc_set_datetime(_Datetime) -> erlang:nif_error(undefined). + + +%%----------------------------------------------------------------------------- +%% @param GPIO pin to read +%% @returns the level of the GPIO pin +%% @doc Read a GPIO of the CYW43. +%% This function is only available on Pico-W. +%% @end +%%----------------------------------------------------------------------------- +-spec cyw43_arch_gpio_get(GPIO :: 0..2) -> 0..1. +cyw43_arch_gpio_get(_GPIO) -> + erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @param GPIO pin to write +%% @param Level value to write +%% @doc Write a GPIO of the CYW43. +%% This function is only available on Pico-W. It is typically used to +%% drive the on-board LED. +%% @end +%%----------------------------------------------------------------------------- +-spec cyw43_arch_gpio_put(GPIO :: 0..2, Level :: 0..1) -> ok. +cyw43_arch_gpio_put(_GPIO, _Level) -> + erlang:nif_error(undefined). diff --git a/src/platforms/rp2040/pico_sdk_import.cmake b/src/platforms/rp2040/pico_sdk_import.cmake index b4622015d..c60e800f3 100644 --- a/src/platforms/rp2040/pico_sdk_import.cmake +++ b/src/platforms/rp2040/pico_sdk_import.cmake @@ -58,14 +58,14 @@ if (NOT PICO_SDK_PATH) FetchContent_Declare( pico_sdk GIT_REPOSITORY https://github.com/pguyot/pico-sdk # revert to raspberrypi once PR 1101 is merged - GIT_TAG w46/condition-variables # revert to master once PR 1101 is merged + GIT_TAG v1.5.1+conditional-variables # revert to master once PR 1101 is merged GIT_SUBMODULES_RECURSE FALSE ) else () FetchContent_Declare( pico_sdk GIT_REPOSITORY https://github.com/pguyot/pico-sdk # revert to raspberrypi once PR 1101 is merged - GIT_TAG w46/condition-variables # revert to master once PR 1101 is merged + GIT_TAG v1.5.1+conditional-variables # revert to master once PR 1101 is merged ) endif () diff --git a/src/platforms/rp2040/src/lib/CMakeLists.txt b/src/platforms/rp2040/src/lib/CMakeLists.txt index d3b1f081f..265c13d47 100644 --- a/src/platforms/rp2040/src/lib/CMakeLists.txt +++ b/src/platforms/rp2040/src/lib/CMakeLists.txt @@ -64,4 +64,8 @@ if (NOT AVM_USE_32BIT_FLOAT) target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_double) endif() +if (PICO_CYW43_SUPPORTED) + target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_cyw43_arch_lwip_threadsafe_background) +endif() + target_link_options(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC -Wl,-u -Wl,gpio_nif) diff --git a/src/platforms/rp2040/src/lib/lwipopts.h b/src/platforms/rp2040/src/lib/lwipopts.h new file mode 100644 index 000000000..9f224e102 --- /dev/null +++ b/src/platforms/rp2040/src/lib/lwipopts.h @@ -0,0 +1,102 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2023 Paul Guyot + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// See https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html + +#define NO_SYS 1 +#define LWIP_SOCKET 0 +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions +#define MEM_LIBC_MALLOC 0 +#endif +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +#endif + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define INET_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define RAW_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define SYS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define PPP_DEBUG LWIP_DBG_OFF +#define SLIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF + +#endif /* __LWIPOPTS_H__ */ \ No newline at end of file diff --git a/src/platforms/rp2040/src/lib/platform_nifs.c b/src/platforms/rp2040/src/lib/platform_nifs.c index 11990825e..b3f81aaca 100644 --- a/src/platforms/rp2040/src/lib/platform_nifs.c +++ b/src/platforms/rp2040/src/lib/platform_nifs.c @@ -31,6 +31,10 @@ #include #include +#ifdef LIB_PICO_CYW43_ARCH +#include +#endif + #pragma GCC diagnostic pop #include "gpiodriver.h" @@ -134,6 +138,27 @@ static term nif_pico_rtc_set_datetime(Context *ctx, int argc, term argv[]) return OK_ATOM; } +#ifdef LIB_PICO_CYW43_ARCH +static term nif_pico_cyw43_arch_gpio_get(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(ctx); + VALIDATE_VALUE(argv[0], term_is_integer); + bool val = cyw43_arch_gpio_get(term_to_int(argv[0])); + return term_from_int(val); +} + +static term nif_pico_cyw43_arch_gpio_put(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(ctx); + VALIDATE_VALUE(argv[0], term_is_integer); + VALIDATE_VALUE(argv[1], term_is_integer); + cyw43_arch_gpio_put(term_to_int(argv[0]), term_to_int(argv[1])); + return OK_ATOM; +} +#endif + static const struct Nif atomvm_platform_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_atomvm_platform @@ -142,6 +167,16 @@ static const struct Nif pico_rtc_set_datetime_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_pico_rtc_set_datetime }; +#ifdef LIB_PICO_CYW43_ARCH +static const struct Nif pico_cyw43_arch_gpio_get_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_pico_cyw43_arch_gpio_get +}; +static const struct Nif pico_cyw43_arch_gpio_put_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_pico_cyw43_arch_gpio_put +}; +#endif const struct Nif *platform_nifs_get_nif(const char *nifname) { @@ -153,6 +188,15 @@ const struct Nif *platform_nifs_get_nif(const char *nifname) TRACE("Resolved platform nif %s ...\n", nifname); return &pico_rtc_set_datetime_nif; } - +#ifdef LIB_PICO_CYW43_ARCH + if (strcmp("pico:cyw43_arch_gpio_get/1", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &pico_cyw43_arch_gpio_get_nif; + } + if (strcmp("pico:cyw43_arch_gpio_put/2", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &pico_cyw43_arch_gpio_put_nif; + } +#endif return nif_collection_resolve_nif(nifname); } diff --git a/src/platforms/rp2040/src/lib/sys.c b/src/platforms/rp2040/src/lib/sys.c index d0a94d3a2..eb459abe0 100644 --- a/src/platforms/rp2040/src/lib/sys.c +++ b/src/platforms/rp2040/src/lib/sys.c @@ -32,6 +32,10 @@ #include #include +#ifdef LIB_PICO_CYW43_ARCH +#include +#endif + #pragma GCC diagnostic pop // libAtomVM @@ -49,10 +53,18 @@ void sys_init_platform(GlobalContext *glb) glb->platform_data = platform; mutex_init(&platform->event_poll_mutex); cond_init(&platform->event_poll_cond); + +#ifdef LIB_PICO_CYW43_ARCH + cyw43_arch_init(); +#endif } void sys_free_platform(GlobalContext *glb) { +#ifdef LIB_PICO_CYW43_ARCH + cyw43_arch_deinit(); +#endif + struct RP2040PlatformData *platform = glb->platform_data; free(platform); } diff --git a/src/platforms/rp2040/src/main.c b/src/platforms/rp2040/src/main.c index 2a0b2abf1..f907506dc 100644 --- a/src/platforms/rp2040/src/main.c +++ b/src/platforms/rp2040/src/main.c @@ -43,8 +43,8 @@ extern char __flash_binary_end; -#define LIB_AVM ((void *) 0x10080000) -#define MAIN_AVM ((void *) 0x100A0000) +#define LIB_AVM ((void *) 0x10100000) +#define MAIN_AVM ((void *) 0x10180000) #define ATOMVM_BANNER \ "\n" \ diff --git a/src/platforms/rp2040/tests/test_erl_sources/CMakeLists.txt b/src/platforms/rp2040/tests/test_erl_sources/CMakeLists.txt index af1bf2f94..00a0ab228 100644 --- a/src/platforms/rp2040/tests/test_erl_sources/CMakeLists.txt +++ b/src/platforms/rp2040/tests/test_erl_sources/CMakeLists.txt @@ -59,7 +59,7 @@ add_custom_command( COMMAND HostAtomVM-prefix/src/HostAtomVM-build/tools/uf2tool/uf2tool create -o rp2040_test_modules.uf2 - -s 0x100A0000 "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm" + -s 0x10100000 "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm" DEPENDS HostAtomVM "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm" diff --git a/src/platforms/rp2040/tests/test_main.c b/src/platforms/rp2040/tests/test_main.c index 1158346b1..47be57014 100644 --- a/src/platforms/rp2040/tests/test_main.c +++ b/src/platforms/rp2040/tests/test_main.c @@ -89,7 +89,7 @@ static void unity_run_all_tests() } } -#define MAIN_AVM ((void *) 0x100A0000) +#define MAIN_AVM ((void *) 0x10100000) static term avm_test_case(const char *test_module) {