From 09fc0a49f618ad52476bc155d2ff8fb76a00bf31 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sun, 3 Sep 2023 17:10:14 +0200 Subject: [PATCH] Add tests for rp2040 powered by rp2040js emulator Signed-off-by: Paul Guyot --- .github/workflows/pico-build.yaml | 17 + CMakeModules/FetchUnity.cmake | 30 + doc/src/build-instructions.md | 18 + src/platforms/rp2040/CMakeLists.txt | 1 + src/platforms/rp2040/src/lib/CMakeLists.txt | 2 +- src/platforms/rp2040/tests/.gitignore | 20 + src/platforms/rp2040/tests/CMakeLists.txt | 54 ++ src/platforms/rp2040/tests/package-lock.json | 557 ++++++++++++++++++ .../rp2040/tests/package-lock.json.license | 17 + src/platforms/rp2040/tests/package.json | 12 + .../rp2040/tests/package.json.license | 17 + src/platforms/rp2040/tests/run-tests.ts | 101 ++++ src/platforms/rp2040/tests/test_main.c | 137 +++++ src/platforms/rp2040/tests/tsconfig.json | 3 + .../rp2040/tests/tsconfig.json.license | 17 + 15 files changed, 1002 insertions(+), 1 deletion(-) create mode 100644 CMakeModules/FetchUnity.cmake create mode 100644 src/platforms/rp2040/tests/.gitignore create mode 100644 src/platforms/rp2040/tests/CMakeLists.txt create mode 100644 src/platforms/rp2040/tests/package-lock.json create mode 100644 src/platforms/rp2040/tests/package-lock.json.license create mode 100644 src/platforms/rp2040/tests/package.json create mode 100644 src/platforms/rp2040/tests/package.json.license create mode 100644 src/platforms/rp2040/tests/run-tests.ts create mode 100644 src/platforms/rp2040/tests/test_main.c create mode 100644 src/platforms/rp2040/tests/tsconfig.json create mode 100644 src/platforms/rp2040/tests/tsconfig.json.license diff --git a/.github/workflows/pico-build.yaml b/.github/workflows/pico-build.yaml index a704000f5..40ab683de 100644 --- a/.github/workflows/pico-build.yaml +++ b/.github/workflows/pico-build.yaml @@ -42,3 +42,20 @@ jobs: cd build cmake .. -G Ninja ninja + + - name: Install nvm and nodejs 20 + run: | + set -euo pipefail + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash + source $HOME/.nvm/nvm.sh + nvm install 20 + + - name: Run tests with rp2040js + shell: bash + working-directory: ./src/platforms/rp2040/tests + run: | + set -euo pipefail + source $HOME/.nvm/nvm.sh + nvm use node + npm install + npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2 diff --git a/CMakeModules/FetchUnity.cmake b/CMakeModules/FetchUnity.cmake new file mode 100644 index 000000000..d0d7ce65d --- /dev/null +++ b/CMakeModules/FetchUnity.cmake @@ -0,0 +1,30 @@ +# +# 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 +# + +include(FetchContent) + +FetchContent_Declare( + unity + GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git + GIT_TAG v2.5.2 + GIT_SHALLOW 1 +) + +FetchContent_MakeAvailable(unity) diff --git a/doc/src/build-instructions.md b/doc/src/build-instructions.md index 27bd9eb69..3bea3efc0 100644 --- a/doc/src/build-instructions.md +++ b/doc/src/build-instructions.md @@ -581,6 +581,24 @@ From the root of the project: cmake .. -G Ninja ninja +### Running tests + +Tests for Pico/RP2040 are run on the desktop (or CI) using [rp2040js](https://github.com/wokwi/rp2040js). +Running tests currently require nodejs 20. + +Change directory to the `src/platforms/rp2040/tests` directory under the AtomVM source tree root: + + shell$ cd + shell$ cd src/platforms/rp2040/tests + +Install the emulator and required Javascript dependencies: + + shell$ npm install + +We are assuming tests were built as part of regular build of AtomVM. Run them with the commands: + + shell$ npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2 + ## Building for NodeJS/Web Two different builds are possible, depending on link options: for NodeJS and diff --git a/src/platforms/rp2040/CMakeLists.txt b/src/platforms/rp2040/CMakeLists.txt index a2dd259e0..cae622e5f 100644 --- a/src/platforms/rp2040/CMakeLists.txt +++ b/src/platforms/rp2040/CMakeLists.txt @@ -57,3 +57,4 @@ set( ) add_subdirectory(src) +add_subdirectory(tests) diff --git a/src/platforms/rp2040/src/lib/CMakeLists.txt b/src/platforms/rp2040/src/lib/CMakeLists.txt index 8ed307952..d3b1f081f 100644 --- a/src/platforms/rp2040/src/lib/CMakeLists.txt +++ b/src/platforms/rp2040/src/lib/CMakeLists.txt @@ -40,7 +40,7 @@ set( ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR} ) -add_library(libAtomVM${PLATFORM_LIB_SUFFIX} ${SOURCE_FILES} ${HEADER_FILES}) +add_library(libAtomVM${PLATFORM_LIB_SUFFIX} STATIC ${SOURCE_FILES} ${HEADER_FILES}) target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11) if(CMAKE_COMPILER_IS_GNUCC) target_compile_options(libAtomVM${PLATFORM_LIB_SUFFIX} PRIVATE -Wall -pedantic -Wextra) diff --git a/src/platforms/rp2040/tests/.gitignore b/src/platforms/rp2040/tests/.gitignore new file mode 100644 index 000000000..732545203 --- /dev/null +++ b/src/platforms/rp2040/tests/.gitignore @@ -0,0 +1,20 @@ +# +# 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 +# +node_modules diff --git a/src/platforms/rp2040/tests/CMakeLists.txt b/src/platforms/rp2040/tests/CMakeLists.txt new file mode 100644 index 000000000..b5263f7bc --- /dev/null +++ b/src/platforms/rp2040/tests/CMakeLists.txt @@ -0,0 +1,54 @@ +# +# 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 +# + +cmake_minimum_required (VERSION 3.14) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../../CMakeModules") +include(FetchUnity) + +add_executable(rp2040_tests test_main.c) + +target_compile_features(rp2040_tests PUBLIC c_std_11) +if(CMAKE_COMPILER_IS_GNUCC) + target_compile_options(rp2040_tests PUBLIC -ggdb) + target_compile_options(rp2040_tests PRIVATE -Wall -pedantic -Wextra) +endif() + +# libAtomVM needs to find Pico's platform_smp.h header +set(HAVE_PLATFORM_SMP_H ON) +target_link_libraries(rp2040_tests PUBLIC libAtomVM) +# Also add lib where platform_smp.h header is +target_include_directories(libAtomVM PUBLIC ../src/lib) + +target_link_libraries(rp2040_tests PUBLIC hardware_regs pico_stdlib pico_binary_info unity) + +set( + PLATFORM_LIB_SUFFIX + ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR} +) + +target_link_libraries(rp2040_tests PRIVATE libAtomVM${PLATFORM_LIB_SUFFIX}) + +# could not get rp2040js to work with stdio USB +pico_enable_stdio_usb(AtomVM 0) +pico_enable_stdio_uart(AtomVM 1) + +# create map/bin/hex/uf2 file in addition to ELF. +pico_add_extra_outputs(rp2040_tests) diff --git a/src/platforms/rp2040/tests/package-lock.json b/src/platforms/rp2040/tests/package-lock.json new file mode 100644 index 000000000..6a1ee6794 --- /dev/null +++ b/src/platforms/rp2040/tests/package-lock.json @@ -0,0 +1,557 @@ +{ + "name": "rp2040_tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rp2040_tests", + "dependencies": { + "@tsconfig/node20": "^20.1.2", + "rp2040js": "github:mingpepe/rp2040js#core1", + "sync-fetch": "^0.5.2", + "tsx": "^3.12.8", + "typescript": "^5.2.2", + "uf2": "^1.0.0" + } + }, + "node_modules/@esbuild-kit/cjs-loader": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz", + "integrity": "sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==", + "dependencies": { + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" + } + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.2.2.tgz", + "integrity": "sha512-Ub6LaRaAgF80dTSzUdXpFLM1pVDdmEVB9qb5iAzSpyDlX/mfJTFGOnZ516O05p5uWWteNviMKi4PAyEuRxI5gA==", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz", + "integrity": "sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==", + "dependencies": { + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz", + "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.0.tgz", + "integrity": "sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rp2040js": { + "version": "0.17.8", + "resolved": "git+ssh://git@github.com/mingpepe/rp2040js.git#23bf9d337b9dbbc69fa22b0689f3736553c902b3", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sync-fetch": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.5.2.tgz", + "integrity": "sha512-6gBqqkHrYvkH65WI2bzrDwrIKmt3U10s4Exnz3dYuE5Ah62FIfNv/F63inrNhu2Nyh3GH5f42GKU3RrSJoaUyQ==", + "dependencies": { + "node-fetch": "^2.6.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tsx": { + "version": "3.12.8", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.8.tgz", + "integrity": "sha512-Lt9KYaRGF023tlLInPj8rgHwsZU8qWLBj4iRXNWxTfjIkU7canGL806AqKear1j722plHuiYNcL2ZCo6uS9UJA==", + "dependencies": { + "@esbuild-kit/cjs-loader": "^2.4.2", + "@esbuild-kit/core-utils": "^3.2.2", + "@esbuild-kit/esm-loader": "^2.5.5" + }, + "bin": { + "tsx": "dist/cli.js" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uf2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uf2/-/uf2-1.0.0.tgz", + "integrity": "sha512-eLBWi1GZ2UJUOiaDctEXsBdMKZvauitLfDtyumu8L3amkfurOJWbI505crvCeyyXK+4gm12jpImkCALgaM8kig==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/src/platforms/rp2040/tests/package-lock.json.license b/src/platforms/rp2040/tests/package-lock.json.license new file mode 100644 index 000000000..08f64bd43 --- /dev/null +++ b/src/platforms/rp2040/tests/package-lock.json.license @@ -0,0 +1,17 @@ +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 diff --git a/src/platforms/rp2040/tests/package.json b/src/platforms/rp2040/tests/package.json new file mode 100644 index 000000000..7f7ead2b6 --- /dev/null +++ b/src/platforms/rp2040/tests/package.json @@ -0,0 +1,12 @@ +{ + "name": "rp2040_tests", + "description": "Tests using rp2040js emulator", + "dependencies": { + "@tsconfig/node20": "^20.1.2", + "rp2040js": "github:mingpepe/rp2040js#core1", + "sync-fetch": "^0.5.2", + "tsx": "^3.12.8", + "typescript": "^5.2.2", + "uf2": "^1.0.0" + } +} diff --git a/src/platforms/rp2040/tests/package.json.license b/src/platforms/rp2040/tests/package.json.license new file mode 100644 index 000000000..08f64bd43 --- /dev/null +++ b/src/platforms/rp2040/tests/package.json.license @@ -0,0 +1,17 @@ +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 diff --git a/src/platforms/rp2040/tests/run-tests.ts b/src/platforms/rp2040/tests/run-tests.ts new file mode 100644 index 000000000..e39aa0fd1 --- /dev/null +++ b/src/platforms/rp2040/tests/run-tests.ts @@ -0,0 +1,101 @@ +/* + * 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 + */ + +import * as fs from "fs"; +import { RP2040, ConsoleLogger, LogLevel } from "rp2040js"; + +import { decodeBlock } from "uf2"; +import fetch from "sync-fetch"; + +const FLASH_START_ADDRESS = 0x10000000; + +function loadBootrom(rp2040: RP2040) { + const ROM_ELF_URL = + "https://github.com/raspberrypi/pico-bootrom/releases/download/b1/b1.elf"; + const response = fetch(ROM_ELF_URL); + if (!response.ok) { + throw new Error(`Error fetching bootrom: ${response.statusText}`); + } + // Skip ELF header and only keep 16 KB + const body = response.arrayBuffer(); + const bootrom: Uint32Array = new Uint32Array(body.slice(0x10000, 0x14000)); + rp2040.loadBootrom(bootrom); +} + +function loadUF2(filename: string, rp2040: RP2040) { + const file = fs.openSync(filename, "r"); + const buffer = new Uint8Array(512); + while (fs.readSync(file, buffer) === buffer.length) { + const block = decodeBlock(buffer); + const { flashAddress, payload } = block; + rp2040.flash.set(payload, flashAddress - FLASH_START_ADDRESS); + } + fs.closeSync(file); +} + +function loadAvm(filename: string, rp2040: RP2040) { + const file = fs.openSync(filename, "r"); + const buffer = new Uint8Array(512); + let addr = 0x100a0000; + while (fs.readSync(file, buffer) > 0) { + rp2040.flash.set(buffer, addr - FLASH_START_ADDRESS); + addr += buffer.length; + } + fs.closeSync(file); +} + +const mcu = new RP2040(); +loadBootrom(mcu); + +for (let arg of process.argv.slice(2)) { + if (arg.endsWith(".uf2")) { + loadUF2(arg, mcu); + } else if (arg.endsWith(".avm")) { + loadAvm(arg, mcu); + } else { + console.error(`Unknown argument ${arg}`); + console.error( + `Syntax: ${process.argv[0]} ${process.argv[1]} [file.uf2 [file2.uf2 ...]]`, + ); + } +} + +mcu.logger = new ConsoleLogger(LogLevel.Error); + +let currentLine = ""; +mcu.uart[0].onByte = (value) => { + process.stdout.write(new Uint8Array([value])); + + const char = String.fromCharCode(value); + if (char === "\n") { + if (currentLine === "OK" || currentLine === "OK\r") { + process.exit(0); + } else if (currentLine === "FAIL" || currentLine === "FAIL\r") { + process.exit(1); + } + currentLine = ""; + } else { + currentLine += char; + } +}; + +mcu.core0.PC = 0x10000000; +mcu.core1.PC = 0x10000000; +mcu.execute(); diff --git a/src/platforms/rp2040/tests/test_main.c b/src/platforms/rp2040/tests/test_main.c new file mode 100644 index 000000000..33c8d3e10 --- /dev/null +++ b/src/platforms/rp2040/tests/test_main.c @@ -0,0 +1,137 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2022 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 + */ + +#include +#include + +#include "unity.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include + +#include "platform_defaultatoms.h" +#include "rp2040_sys.h" + +#ifndef AVM_NO_SMP +#include "smp.h" +#endif + +const struct Nif *platform_nifs_get_nif(const char *nifname); + +void setUp() +{ +} + +void tearDown() +{ +} + +struct TestCase +{ + const char *name; + void (*func)(void); + int line; + struct TestCase *next; +}; + +struct TestCase *test_case_list = NULL; + +#define TEST_CASE(TEST_NAME) \ + static void TEST_NAME##_test(void); \ + struct TestCase TEST_NAME##_test_case = { \ + .name = #TEST_NAME, \ + .func = TEST_NAME##_test, \ + .line = __LINE__ \ + }; \ + __attribute__((constructor)) void TEST_NAME##_test_case_register() \ + { \ + TEST_NAME##_test_case.next = test_case_list; \ + test_case_list = &TEST_NAME##_test_case; \ + } \ + static void TEST_NAME##_test(void) + +static void unity_run_all_tests() +{ + for (struct TestCase *test_case = test_case_list; test_case != NULL; test_case = test_case->next) { + UnityDefaultTestRun(test_case->func, test_case->name, test_case->line); + } +} + +TEST_CASE(test_atomvm_platform0) +{ + const struct Nif *nif = platform_nifs_get_nif("atomvm:platform/0"); + TEST_ASSERT_EQUAL_INT(PICO_ATOM, nif->nif_ptr(NULL, 0, NULL)); +} + +TEST_CASE(test_atomvm_missing0) +{ + const struct Nif *nif = platform_nifs_get_nif("atomvm:missing/0"); + TEST_ASSERT_EQUAL_PTR(NULL, nif); +} + +#ifndef AVM_NO_SMP +TEST_CASE(atomvm_smp_0) +{ + int cores = smp_get_online_processors(); + TEST_ASSERT_EQUAL_INT(2, cores); +} +#endif + +/* newlib stubs to get AVM_ABORT to work */ +pid_t _getpid() +{ + return 1; /* init :-) */ +} + +int _kill(pid_t pid, int sig) +{ + UNUSED(pid); + if (sig == SIGABRT) { + TEST_FAIL_MESSAGE("Aborted"); + } else { + char signal_msg[25]; + snprintf(signal_msg, sizeof(signal_msg), "Unknown signal %d", sig); + TEST_FAIL_MESSAGE(signal_msg); + } + return 0; +} + +int main() +{ + stdio_init_all(); + UNITY_BEGIN(); + unity_run_all_tests(); + return UNITY_END(); +} diff --git a/src/platforms/rp2040/tests/tsconfig.json b/src/platforms/rp2040/tests/tsconfig.json new file mode 100644 index 000000000..b99454516 --- /dev/null +++ b/src/platforms/rp2040/tests/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json" +} diff --git a/src/platforms/rp2040/tests/tsconfig.json.license b/src/platforms/rp2040/tests/tsconfig.json.license new file mode 100644 index 000000000..08f64bd43 --- /dev/null +++ b/src/platforms/rp2040/tests/tsconfig.json.license @@ -0,0 +1,17 @@ +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