diff --git a/.github/ISSUE_TEMPLATE/compilation_bug_report.md b/.github/ISSUE_TEMPLATE/compilation_bug_report.md new file mode 100644 index 00000000..79402ed0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/compilation_bug_report.md @@ -0,0 +1,22 @@ +--- +name: Compilation bug report +about: Sketch doesn't compile +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Target board + cli verbose compilation output** +**Full verbose** compilation output, ideally with `arduino-cli` invocation or from IDE 2.3.3+ +Issues without the full verbose output will be discarded as invalid. + +**Mandatory: attach the sketch** +or a Minimal reproducible example +Issues without the sketch will be discarded as invalid. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/miscellaneous_bug_report.md b/.github/ISSUE_TEMPLATE/miscellaneous_bug_report.md new file mode 100644 index 00000000..7a24d7f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/miscellaneous_bug_report.md @@ -0,0 +1,22 @@ +--- +name: Miscellaneous bug report +about: Sketch compiles but doesn't upload, or similar issues +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Target board + cli verbose compilation output** +**Full verbose** compilation output, ideally with `arduino-cli` invocation or from IDE 2.3.3+ +Issues without the full verbose output will be discarded as invalid. + +**cli/IDE verbose upload output** +**Full verbose** upload output, ideally with `arduino-cli` invocation or from IDE 2.3.3+ +Issues without the full verbose output will be discarded as invalid. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/runtime_bug_report.md b/.github/ISSUE_TEMPLATE/runtime_bug_report.md new file mode 100644 index 00000000..a4ba6ddf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/runtime_bug_report.md @@ -0,0 +1,32 @@ +--- +name: Runtime bug report +about: Sketch compiles and uploads but doesn't run correctly (or doesn't run at all) +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Target board + cli verbose compilation output** +**Full verbose** compilation output, ideally with `arduino-cli` invocation or from IDE 2.3.3+ +Issues without the full verbose output will be discarded as invalid. + +**Output of Serial Monitor** +1. If you have an USB-to-Serial adapter, paste the complete output of the console crash, starting from +```*** Booting Zephyr OS build v3.7...``` +2. If you don't, compile the sketch in `Debug` mode (see *Troubleshooting section* in README) and paste the output after invoking `sketch` command +Runtime issues without the **full output** will be discarded as invalid. + +**Output of readelf** +You can find the loaction of the elf file by compiling in Verbose mode and looking near the end of the compilation output (after `Linking everything together..`) +Paste (or attach) the output of `arm-none-eabi-readelf -a $your_sketch_elf_file` + +**Optional: attach the elf file** + +**Optional: attach the sketch** + +**Additional context** +Add any other context about the problem here. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 956cc9fe..ba541799 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,44 +1,40 @@ -name: Build +name: Build native Zephyr samples on: [push, pull_request] jobs: build: + name: Build native Zephyr samples runs-on: ubuntu-latest container: zephyrprojectrtos/ci:latest env: CMAKE_PREFIX_PATH: /opt/toolchains + CCACHE_IGNOREOPTIONS: -specs=* + REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }} steps: - - name: Checkout - uses: actions/checkout@v4 - with: - path: Arduino-Zephyr-API - - name: Initialize - working-directory: Arduino-Zephyr-API run: | - west init -m https://github.com/zephyrproject-rtos/gsoc-2022-arduino-core.git - west update - git clone https://github.com/arduino/ArduinoCore-API.git ArduinoCore-API - cp -r ArduinoCore-API/api modules/lib/Arduino-Zephyr-API/cores/arduino/. + mkdir build && cd build + west init -m https://github.com/${{ env.REPOSITORY }} --mr ${{ env.BRANCH }} + west update -o=--filter=tree:0 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + verbose: 1 - name: Build fade - working-directory: Arduino-Zephyr-API + working-directory: build run: | - west build -p -b arduino_nano_33_ble_sense samples/fade + west build -p -b arduino_nano_33_ble//sense modules/lib/ArduinoCore-zephyr/samples/fade - name: Build i2cdemo - working-directory: Arduino-Zephyr-API + working-directory: build run: | - west build -p -b arduino_nano_33_ble_sense samples/i2cdemo + west build -p -b ek_ra8d1 modules/lib/ArduinoCore-zephyr/samples/i2cdemo - name: Build adc - working-directory: Arduino-Zephyr-API + working-directory: build run: | - west build -p -b beagleconnect_freedom samples/analog_input - - - name: Archive firmware - uses: actions/upload-artifact@v2 - with: - name: firmware - path: Arduino-Zephyr-API/build/zephyr/zephyr.* + west build -p -b arduino_nano_33_ble/nrf52840/sense modules/lib/ArduinoCore-zephyr/samples/analog_input diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml index bce1fb66..e0cb4305 100644 --- a/.github/workflows/license_check.yml +++ b/.github/workflows/license_check.yml @@ -15,7 +15,7 @@ jobs: with: directory-to-scan: 'scan/' - name: Artifact Upload - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: scancode path: ./artifacts diff --git a/.github/workflows/package_core.yml b/.github/workflows/package_core.yml new file mode 100644 index 00000000..2fbdbf6e --- /dev/null +++ b/.github/workflows/package_core.yml @@ -0,0 +1,248 @@ +name: Package, test and upload core + +on: + - push + - pull_request + +jobs: + + package-core: + name: Build and package core + runs-on: ubuntu-latest + env: + CCACHE_IGNOREOPTIONS: -specs=* + outputs: + CORE_TAG: ${{ env.CORE_TAG }} + CORE_ARTIFACT: ${{ env.CORE_ARTIFACT }} + BOARD_VARIANTS: ${{ env.BOARD_VARIANTS }} + steps: + - name: Install OS dependencies + working-directory: /opt + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends git cmake wget python3-pip ninja-build ccache + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Initialize Zephyr environment + run: | + ./extra/bootstrap.sh -o=--filter=tree:0 + echo "CORE_TAG=$(git describe --always)" >> "$GITHUB_ENV" + echo "CORE_ARTIFACT=ArduinoCore-zephyr-$(git describe --always)" >> "$GITHUB_ENV" + echo "BOARD_VARIANTS=$(extra/get_board_details.sh | jq -cr 'sort_by(.variant)')" >> "$GITHUB_ENV" + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + verbose: 1 + + - name: Build variants + shell: bash + run: | + ./extra/build_all.sh -f + + - name: Package core + run: | + ./extra/package_core.sh ${{ env.CORE_TAG }} + + - name: Archive core + uses: actions/upload-artifact@v4 + with: + name: ${{ env.CORE_ARTIFACT }} + path: distrib/${{ env.CORE_ARTIFACT }}.tar.bz2 + + test-core: + name: Test ${{ matrix.board }} board + runs-on: ubuntu-latest + needs: package-core + strategy: + matrix: + include: + ${{ fromJSON( needs.package-core.outputs.BOARD_VARIANTS ) }} + fail-fast: false + env: + FQBN: arduino:zephyr:${{ matrix.board }} + REPORT_FILE: arduino-zephyr-${{ matrix.board }}.json + steps: + - uses: actions/download-artifact@v4 + with: + name: ${{ needs.package-core.outputs.CORE_ARTIFACT }} + + - name: Set up core + run: | + tar xf ${{ needs.package-core.outputs.CORE_ARTIFACT }}.tar.bz2 + + - name: Create Blink sketch + run: | + mkdir Blink/ + wget -nv https://raw.githubusercontent.com/arduino/arduino-examples/refs/heads/main/examples/01.Basics/Blink/Blink.ino -P Blink/ + + - name: Compile Blink for ${{ matrix.board }} + uses: pillo79/compile-sketches@main + with: + fqbn: ${{ env.FQBN }} + platforms: | + # Use Board Manager to install the latest release of Arduino Zephyr Boards to get the toolchain + - name: "arduino:zephyr" + source-url: "https://downloads.arduino.cc/packages/package_zephyr_index.json" + - name: "arduino:zephyr" + source-path: "ArduinoCore-zephyr" + sketch-paths: Blink + cli-compile-flags: | + - '--build-property' + - 'compiler.c.extra_flags=-Wno-type-limits -Wno-missing-field-initializers' + - '--build-property' + - 'compiler.cpp.extra_flags=-Wno-type-limits -Wno-missing-field-initializers' + verbose: 'false' + enable-deltas-report: 'false' + enable-warnings-report: 'true' + enable-warnings-log: 'true' + + - name: Get job ID + id: job_id + if: ${{ success() || failure() }} + uses: actions/github-script@main + with: + script: | + const { data: workflow_run } = await github.rest.actions.listJobsForWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + const job_name = `Test ${{ matrix.board }} board` + return workflow_run.jobs.find((job) => job.name === job_name).id; + + - name: Prepare log + if: ${{ success() || failure() }} + run: | + sed -i -e 's!/home/runner/.arduino15/packages/arduino/hardware/zephyr/[^/]*/!!g' sketches-reports/${REPORT_FILE} + cat sketches-reports/${REPORT_FILE} | jq -cr ".boards[0].sketches[0] += { job_id: ${{ steps.job_id.outputs.result }} }" > ${REPORT_FILE} && mv ${REPORT_FILE} sketches-reports/ + + - uses: actions/upload-artifact@v4 + if: ${{ success() || failure() }} + with: + name: test-report-${{ needs.package-core.outputs.CORE_TAG }}-${{ matrix.board }} + path: sketches-reports/* + + collect-logs: + name: Collect logs + runs-on: ubuntu-latest + needs: + - package-core + - test-core + if: ${{ !cancelled() && needs.package-core.result == 'success' }} + env: + BOARD_VARIANTS: ${{ needs.package-core.outputs.BOARD_VARIANTS }} + steps: + - uses: actions/download-artifact@v4 + with: + path: . + pattern: test-report-* + merge-multiple: true + + - run: | + echo "### Core test results:" >> "$GITHUB_STEP_SUMMARY" + jq -c '.[]' <<< "$BOARD_VARIANTS" | while read -r BOARD_VARIANT; do + BOARD=$(echo $BOARD_VARIANT | jq -cr '.board') + VARIANT=$(echo $BOARD_VARIANT | jq -cr '.variant') + FQBN="arduino:zephyr:$BOARD" + REPORT_FILE="arduino-zephyr-$BOARD.json" + if [ ! -f $REPORT_FILE ]; then + echo "* :x: $BOARD (`$VARIANT`) - No report found?" >> "$GITHUB_STEP_SUMMARY" + else + REPORT=$(jq -cr '.boards[0].sketches[0]' $REPORT_FILE) + JOB_ID=$(echo $REPORT | jq -cr '.job_id') + JOB_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/job/${JOB_ID}#step:5:2" + if ! $(echo $REPORT | jq -cr '.compilation_success') ; then + echo "* :x: [$BOARD]($JOB_URL) (\`$VARIANT\`) - Build failed" >> "$GITHUB_STEP_SUMMARY" + else + WARNINGS=$(echo $REPORT | jq -cr '.warnings.current.absolute // 0') + if [ $WARNINGS -eq 0 ]; then + echo "* :white_check_mark: $BOARD (\`$VARIANT\`) - Build successful" >> "$GITHUB_STEP_SUMMARY" + else + echo >> "$GITHUB_STEP_SUMMARY" + echo "
   :warning: $BOARD ($VARIANT) - $WARNINGS warnings:" >> "$GITHUB_STEP_SUMMARY" + echo >> "$GITHUB_STEP_SUMMARY" + echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY" + echo $REPORT | jq -cr '.warnings_log[]' >> "$GITHUB_STEP_SUMMARY" + echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY" + echo >> "$GITHUB_STEP_SUMMARY" + echo "
" >> "$GITHUB_STEP_SUMMARY" + echo >> "$GITHUB_STEP_SUMMARY" + fi + fi + fi + done + + - name: Clean up intermediate artifacts + uses: geekyeggo/delete-artifact@v5.1.0 + with: + name: test-report-* + failOnError: false + + publish-core: + name: Publish core + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.repository == 'arduino/ArduinoCore-zephyr' }} + needs: + - package-core + - test-core + environment: production + permissions: + id-token: write + contents: read + env: + CORE_ARTIFACT: ${{ needs.package-core.outputs.CORE_ARTIFACT }} + ARTIFACT_FILE: ${{ needs.package-core.outputs.CORE_ARTIFACT }}.tar.bz2 + steps: + - uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_ARTIFACT }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.IAM_ROLE }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Upload artifact + run: aws s3 cp ${{ env.ARTIFACT_FILE }} s3://${{ secrets.S3_BUCKET }}/ + + publish-json: + name: Publish json + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.repository == 'arduino/ArduinoCore-zephyr' }} + needs: + - package-core + - test-core + - publish-core + env: + CORE_ARTIFACT: ${{ needs.package-core.outputs.CORE_ARTIFACT }} + ARTIFACT_FILE: ${{ needs.package-core.outputs.CORE_ARTIFACT }}.tar.bz2 + CORE_TAG: ${{ needs.package-core.outputs.CORE_TAG }} + PACKAGE_INDEX_JSON: zephyr-core-${{ needs.package-core.outputs.CORE_TAG }}.json + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + sparse-checkout: | + extra/gen_package_index_json.sh + extra/zephyr-core-template.json + + - uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_ARTIFACT }} + + # uses: ARTIFACT_FILE CORE_TAG PACKAGE_INDEX_JSON + - name: Prepare package index snippet + run: ./extra/gen_package_index_json.sh + + - name: Archive package index snippet + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PACKAGE_INDEX_JSON }} + path: ${{ env.PACKAGE_INDEX_JSON }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..09974049 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/build/ +/distrib/ +/firmwares/* +/venv/ +llext-edk/ +cflags.txt +cxxflags.txt +includes.txt +provides.ld diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d50d173..47fd59e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,18 @@ # SPDX-License-Identifier: Apache-2.0 +string(REPLACE "__" "_" NORMALIZED_BOARD_TARGET "${NORMALIZED_BOARD_TARGET}") + if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/variants/${BOARD}) set(variant_dir variants/${BOARD}) -elseif (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/variants/${BOARD}${NORMALIZED_BOARD_QUALIFIERS}) - set(variant_dir variants/${BOARD}${NORMALIZED_BOARD_QUALIFIERS}) +elseif (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/variants/${NORMALIZED_BOARD_TARGET}) + set(variant_dir variants/${NORMALIZED_BOARD_TARGET}) else() - message(FATAL_ERROR "Variant dir not found: variants/${BOARD}, variants/${BOARD}${NORMALIZED_BOARD_QUALIFIERS}") +if (CONFIG_ARDUINO_API) + message(FATAL_ERROR "Variant dir not found: variants/${BOARD}, variants/${NORMALIZED_BOARD_TARGET}") +endif() endif() -if (CONFIG_ARDUINO_API) +if (CONFIG_ARDUINO_API AND NOT CONFIG_LLEXT) add_subdirectory(cores) add_subdirectory(libraries) zephyr_include_directories(${variant_dir}) diff --git a/Kconfig b/Kconfig index 27f9fb3c..237a1fc1 100644 --- a/Kconfig +++ b/Kconfig @@ -31,3 +31,23 @@ config ARDUINO_ENTRY default y endif + +if USB_DEVICE_STACK_NEXT + +config USB_DEVICE_PRODUCT + string "USB Device Product" + default "Arduino Generic board" + +config USB_DEVICE_MANUFACTURER + string "USB Device Manufacturer" + default "Arduino" + +config USB_DEVICE_VID + hex "USB Device Vendor ID" + default 0x2341 + +config USB_DEVICE_PID + hex "USB Device Product ID" + default 0x0001 + +endif diff --git a/README.gsoc.md b/README.gsoc.md new file mode 100644 index 00000000..2d230909 --- /dev/null +++ b/README.gsoc.md @@ -0,0 +1,55 @@ +# GSoC 2022 Project: Arduino Core API module for Zephyr + +![](https://dhruvag2000.github.io/Blog-GSoC22/assets/images/website_header.png) + +The **Arduino Core API** module for zephyr leverages the power of Zephyr under an Arduino-C++ style abtraction layer thus helping zephyr new-comers to start using it without worrying about learning about new APIs and libraries. See the project documentation folder for detailed documentation on these topics: + +* [Using external Arduino Libraries](/documentation/arduino_libs.md) +* [Adding custom boards/ variants](/documentation/variants.md) + +## Adding Arduino Core API to Zephyr + +* **Pre requisites:** It is assumed that you have zephyrproject configured and installed on your system as per the official [Get Started Guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html). The recommended path to install is `~/zephyrproject` as specified in the guide. If you have zephyr installed in a custom path you may need to make changes to the CMakeLists.txt file in the sample code directory when building these samples. + +* Add following entry to `west.yml` file in `manifest/projects` subtree of Zephyr: +``` +# Arduino API repository. +- name: Arduino-Core-Zephyr + path: modules/lib/Arduino-Zephyr-API + revision: main + url: https://github.com/zephyrproject-rtos/gsoc-2022-arduino-core +``` + +* Then, clone the repository by running + +```sh +west update +``` + +* **Note:** For ***Linux users only*** there exists an ``install.sh`` script in this project that can be run to quickly link the ArduinoCore-API to this module. +If you are able to run that script successfully then you can skip the next steps. + +* To "complete" the core you need to copy or symlink the api folder from the [ArduinoCore-API](https://github.com/arduino/ArduinoCore-API.git) repo to the target's ``cores/arduino`` folder: +```sh +$ git clone git@github.com:arduino/ArduinoCore-API # Any location +$ ln -s ///ArduinoCore-API/api cores/arduino/. +``` +The `cores` folder can be found at `~/zephyrproject/modules/lib/Arduino-Zephyr-API/cores`. + +**Known Bug(s):** + +__NOTE:__ You can skip this step as well if you ran ``install.sh``. + +**Maintainers**: +- [DhruvaG2000](https://github.com/DhruvaG2000) +- [soburi](https://github.com/soburi) +- [szczys](https://github.com/szczys) +- [beriberikix](https://github.com/beriberikix) +- [alvarowolfx](https://github.com/alvarowolfx) + +## License +Please note that the current license is Apache 2. Previously it was LGPL 2.1 but after careful review it was determined that no LGPL code or derivates was used and the more permissive license was chosen. + +**Additional Links** +* [Official Project Blog](https://dhruvag2000.github.io/Blog-GSoC22/) +* Golioth's Article: [Zephyr + Arduino: a Google Summer of Code story](https://blog.golioth.io/zephyr-arduino-a-google-summer-of-code-story/) diff --git a/README.md b/README.md index 2d230909..cfbcde69 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,242 @@ -# GSoC 2022 Project: Arduino Core API module for Zephyr +> [!IMPORTANT] +> This core is in **BETA**. πŸ§ͺ +> Features may change, and bugs may be present. Use for testing only and provide feedback to help us improve. +> +> [![Default branch status](https://github.com/arduino/ArduinoCore-zephyr/actions/workflows/package_core.yml/badge.svg?branch=arduino&event=push)](https://github.com/arduino/ArduinoCore-zephyr/actions/workflows/package_core.yml) -![](https://dhruvag2000.github.io/Blog-GSoC22/assets/images/website_header.png) +# 🚧 Arduino Core for Zephyr -The **Arduino Core API** module for zephyr leverages the power of Zephyr under an Arduino-C++ style abtraction layer thus helping zephyr new-comers to start using it without worrying about learning about new APIs and libraries. See the project documentation folder for detailed documentation on these topics: +This repository contains the official implementation of **Arduino Core** for Zephyr RTOS based board. -* [Using external Arduino Libraries](/documentation/arduino_libs.md) -* [Adding custom boards/ variants](/documentation/variants.md) +## 🧐 What is Zephyr? -## Adding Arduino Core API to Zephyr +[Zephyr RTOS](https://zephyrproject.org/) is an open-source, real-time operating system designed for low-power, resource-constrained devices. It's modular, scalable, and supports multiple architectures. -* **Pre requisites:** It is assumed that you have zephyrproject configured and installed on your system as per the official [Get Started Guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html). The recommended path to install is `~/zephyrproject` as specified in the guide. If you have zephyr installed in a custom path you may need to make changes to the CMakeLists.txt file in the sample code directory when building these samples. +![Zephyr RTOS Logo](doc/zephyr_logo.jpg) -* Add following entry to `west.yml` file in `manifest/projects` subtree of Zephyr: +## βš™οΈ Installation + +Install the core and its toolchains via Board Manager: +* Download and install the latest [Arduino IDE](https://www.arduino.cc/en/software) (only versions `2.x.x` are supported). +* Open the *'Settings / Preferences'* window. +* Open the *'Boards Manager'* from the side menu and search for *'Zephyr'*. + * If it doesn’t appear, add the following URL to the *'Additional Boards Manager URLs'* field: `https://downloads.arduino.cc/packages/package_zephyr_index.json` (if you have multiple URLs, separate them with a comma). +* Install the `Arduino Zephyr Boards` platform. + +Alternatively, to install the core using the command line, run the following command with the Arduino CLI: + +```bash +arduino-cli core install arduino:zephyr --additional-urls https://downloads.arduino.cc/packages/package_zephyr_index.json +``` + +## πŸ—οΈ First Use + +To get started with your board: +* Put the board in bootloader mode by double-clicking the RESET button. +* Run the `Burn Bootloader` option from the IDE/CLI. + * Note that due to limitations in the Arduino IDE, you may need to select any programmer from the `Programmers` menu. +* Once the bootloader is installed, you can load your first sketch by placing the board into bootloader mode again. + +> [!NOTE] +> After the initial setup, future sketches will be loaded automatically without needing to reset the board. + +## πŸ”§ Troubleshooting + +### Common Issues + +#### **Q: My Sketch doesn't start (Serial doesn't appear)** +**A:** Connect a USB-to-UART adapter to the default UART (eg. TX0/RX0 on Giga, TX/RX on Nano) and read the error message (with the sketch compiled in `Default` mode). If you don't own a USB-to-UART adapter, compile the sketch in `Debug` mode; this will force the shell to wait until you open the Serial Monitor. Then, run `sketch` command and *probably* you'll be able to read the error (if generated by `llext`). For OS crashes, the USB-to-UART adapter is the only way to collect the crash. + +--- + +#### **Q: I did it and I get the error: ` llext: Undefined symbol with no entry in symbol table ...`** +**A:** This means you are trying to use a Zephyr function which has not yet been exported. Open `llext_exports.c`, add the function you need and recompile/upload the loader. + +--- + +#### **Q: I want to use a Zephyr subsystem which is not compiled in** +**A:** Open the `.conf` file for your board, add the required `CONFIG_`, recompile/upload the loader. + +--- + +#### **Q: I get an OS crash, like ` os: ***** USAGE FAULT *****`** +**A:** This is usually due to a buffer overflow or coding error in the user's own code. However, since the project is still in beta πŸ§ͺ, a [good bug report](#-bug-reporting) could help identify any issues in our code. + +--- + +#### **Q: I get an out of memory error** +**A:** Since collecting bug reports is very important at this time, we are keeping Zephyr's shell enabled to allow loading a full sketch (which requires a large stack). Adjust your board's `.conf` file to reduce the stack size if your platform doesn't have enough RAM. + +## πŸ“š Libraries + +### Included with the core: ### + +### Separately supplied: ### +- **ArduinoBLE**: This library is enabled only for the Arduino Nano 33 BLE. Please use [this branch](https://github.com/facchinm/ArduinoBLE/tree/zephyr_hci) to test it. + +## 🧒 Under the hood + +Unlike traditional Arduino implementations, where the final output is a standalone binary loaded by a bootloader, this core generates a freestanding `elf` file. This file is dynamically loaded by a precompiled Zephyr firmware, referred to as the `loader`. + +The `loader` is responsible for managing the interaction between your sketches and the underlying Zephyr system. After the initial bootloader installation, the `loader` takes over the sketch loading process automatically. + +To ensure flexibility, the `loader` project is designed to be generic. Any necessary modifications for specific boards should be made in the corresponding "DTS overlay" or a special "fixup" file, using appropriate guards to maintain compatibility. + +The behavior of the `loader` can be adjusted through the `Mode` menu of the IDE: +- `Standard`: The sketch is loaded automatically. +- `Debug`: The user must type `sketch` in Zephyr's shell, which is accessible via the default Serial. + +The most important components of this project are: + +* [Zephyr based loader](/loader) +* [LLEXT](https://docs.zephyrproject.org/latest/services/llext/index.html) +* [Actual core](/cores/arduino) with [variants](/variants) and the usual [platform](/platform.txt) and [boards](/boards) files +* [ArduinoCore-API](https://github.com/arduino/ArduinoCore-API) +* [zephyr-sketch-tool](/extra/zephyr-sketch-tool) + +## πŸƒ Shortcut: using the Core in Arduino IDE/CLI without installing Zephyr + +> [!TIP] +> +> If you are only interested in developing features in the [core](/cores/arduino) +> or [libraries](/libraries), and do not want to set up a full Zephyr build +> environment, you can use the [`sync-zephyr-artifacts`](/extra/sync-zephyr-artifacts) +> utility to download a pre-built version of the files needed to compile +> sketches and flash the loader. +> +> To do so, after cloning this repo, compile the `sync-zephyr-artifacts` +> utility via `go build` and run it as `sync-zephyr-artifacts .` to retrieve +> the precompiled files for the current revision of the core. +> +> Next, follow the instructions in [Using the Core in Arduino IDE/CLI](#using-the-core-in-arduino-idecli). +> Remember to [update the loader on your board](#flash-the-loader) as well. + +## πŸ› οΈ Setup a Zephyr build environment + +In this section, we’ll guide you through setting up your environment to work on and update the Zephyr core. + +Shell scripts are available to simplify the installation process (Windows is not supported at the moment πŸ˜”). + +### Clone the repository +```bash +mkdir my_new_zephyr_folder && cd my_new_zephyr_folder +git clone https://github.com/arduino/ArduinoCore-zephyr +``` +### Pre-requirements +Before running the installation script, ensure that Python, `pip` and `venv` are installed on your system. The script will automatically install `west` and manage the necessary dependencies. + +On Ubuntu or similar `apt`-based distros, make sure to run the following command: +```bash +sudo apt install python3-pip python3-setuptools python3-venv build-essential git cmake ninja-build zstd jq ``` -# Arduino API repository. -- name: Arduino-Core-Zephyr - path: modules/lib/Arduino-Zephyr-API - revision: main - url: https://github.com/zephyrproject-rtos/gsoc-2022-arduino-core + +### Run the ```bootstrap``` script +```bash +cd ArduinoCore-zephyr +./extra/bootstrap.sh ``` -* Then, clone the repository by running +This will take care of installing `west`, the Zephyr build tool. It will then +download all packages required for a Zephyr build in addition to the toolchains +in the Zephyr SDK. -```sh -west update +> [!NOTE] +> This core is validated with version v0.17.0 of the SDK. Compatibility with later versions has not been tested yet. + +## πŸ› οΈ Regenerate the compiled core files + +### Build the Loader + +The loader is compiled for each board by running the `./extra/build.sh` script. +The target can be specified either with the Arduino board name (as defined in +boards.txt), or with the Zephyr board name and any additional arguments that +may be required by the Zephyr build system. + +For example, to build for the Arduino Portenta H7, you can use either the +Arduino name: +```bash +./extra/build.sh portentah7 ``` -* **Note:** For ***Linux users only*** there exists an ``install.sh`` script in this project that can be run to quickly link the ArduinoCore-API to this module. -If you are able to run that script successfully then you can skip the next steps. +or the Zephyr board target: -* To "complete" the core you need to copy or symlink the api folder from the [ArduinoCore-API](https://github.com/arduino/ArduinoCore-API.git) repo to the target's ``cores/arduino`` folder: -```sh -$ git clone git@github.com:arduino/ArduinoCore-API # Any location -$ ln -s ///ArduinoCore-API/api cores/arduino/. +```bash +./extra/build.sh arduino_portenta_h7//m7 ``` -The `cores` folder can be found at `~/zephyrproject/modules/lib/Arduino-Zephyr-API/cores`. -**Known Bug(s):** +The firmwares will be copied to the [firmware](/firmware) folder, and the +associated variant will be updated. + +### Flash the Loader + +If the board is fully supported by Zephyr, you can flash the firmware directly onto the board using the following command: +```bash +west flash +``` +This can also be performed via the "Burn bootloader" action in the IDE if the core is properly installed, as detailed below. + +### Using the Core in Arduino IDE/CLI + +After running the `bootstrap.sh` script, you can symlink the core to `$sketchbook/hardware/arduino-git/zephyr`. Once linked, it will appear in the IDE/CLI, and the board's Fully Qualified Board Name (FQBN) will be formatted as `arduino-git:zephyr:name_from_boards_txt`. + +## πŸš€ Adding a new target + +> [!TIP] +> +> While Zephyr supports a lot of different hardware targets, only the few +> currently used by the Arduino core are installed by default. To add the +> support for every Zephyr target to your workspace, run the following +> commands: +> +> ```bash +> . venv/bin/activate +> west config -d manifest.project-filter +> west sdk install --version 0.17.0 +> west update +> ``` + +To add a new board that is already supported by mainline Zephyr with the target `$your_board`, follow these steps: + +* Get the variant name from your board by running `extra/get_variant_name.sh $your_board`. +* Create a folder in the [`variants/`](/variants) directory with the same name as the variant for your new board. +* Create the DTS `.overlay` and Kconfig `.conf` files in that directory. + + The overlay must include: + * A flash partition called `user_sketch`, typically located near the end of the flash. + * A `zephyr,user` section containing the description for GPIOs, Analog, UART, SPI and I2C devices. Feel free to leave some fields empty in case Zephyr support is missing. This will result in some APIs not being available at runtime (eg. `analogWrite` if PWM section is empty). + + The Kconfig file must include any board-specific options required by this target. +* Build the Loader: run `./extra/build.sh $your_board` (with any additional arguments as required) and start debugging the errors. :grin: +* Update the `boards.txt`: add an entry for your board, manually filling the required fields. + + Make sure to set: + * `build.zephyr_target` and `build.zephyr_args` to the arguments used in the `build.sh` call; + * `build.zephyr_hals` to the (space-separated list of) HAL modules required by the board; + * `build.variant` to the variant name identified above. +* Implement touch support: if your board supports the "1200bps touch" method, implement `_on_1200_bps` in a file located inside the variant folder of your board. + +## πŸ› Bug Reporting + +To report a bug, open the [issues](/../../issues) and follow the instructions. Any issue opened without the needed information will be discarded. + +## πŸ™Œ Contributions + +Contributions are always welcome. The preferred way to receive code contribution is by submitting a [Pull request](/../../pulls). + +> [!WARNING] +> At this stage of development, we only accept Pull requests for bug fixes and features. We do **not** accept support for new targets. -__NOTE:__ You can skip this step as well if you ran ``install.sh``. +## πŸ“Œ Upcoming features -**Maintainers**: -- [DhruvaG2000](https://github.com/DhruvaG2000) -- [soburi](https://github.com/soburi) -- [szczys](https://github.com/szczys) -- [beriberikix](https://github.com/beriberikix) -- [alvarowolfx](https://github.com/alvarowolfx) +- [ ] Remove binaries from this repo history (arduino/ArduinoCore-zephyr#102, :warning: will require a clean clone) +- [x] Network: support UDP and TLS +- [ ] USB: switch to `USB_DEVICE_STACK_NEXT` to support PluggableUSB +- [ ] Relocate RODATA in flash to accommodate sketches with large assets +- [ ] Provide better error reporting for failed llext operations +- [ ] Replace [`llext_exports.c`](/loader/llext_exports.c) with proper symbols generation (via includes) +- [ ] Fix corner cases with `std::` includes (like ``) +- [ ] Get rid of all warnings -## License -Please note that the current license is Apache 2. Previously it was LGPL 2.1 but after careful review it was determined that no LGPL code or derivates was used and the more permissive license was chosen. +## 🌟 Acknowledgments -**Additional Links** -* [Official Project Blog](https://dhruvag2000.github.io/Blog-GSoC22/) -* Golioth's Article: [Zephyr + Arduino: a Google Summer of Code story](https://blog.golioth.io/zephyr-arduino-a-google-summer-of-code-story/) +This effort would have been very hard without the [GSoC project](/README.gsoc.md) and the Zephyr community. diff --git a/boards.txt b/boards.txt new file mode 100644 index 00000000..fbfbd13f --- /dev/null +++ b/boards.txt @@ -0,0 +1,566 @@ +menu.debug=Debug +menu.mode=Mode + +giga.name=Arduino Giga R1 +giga.build.core=arduino +giga.build.crossprefix=arm-zephyr-eabi- +giga.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +giga.menu.debug.false=Standard +giga.menu.debug.true=Debug + +giga.menu.debug.false.postbuild_debug= +giga.menu.debug.true.postbuild_debug=-debug + +giga.build.zephyr_target=arduino_giga_r1//m7 +giga.build.zephyr_args=--shield arduino_giga_display_shield +giga.build.zephyr_hals=hal_stm32 +giga.build.variant=arduino_giga_r1_stm32h747xx_m7 +giga.build.mcu=cortex-m7 +giga.build.fpu=-mfpu=fpv5-d16 +giga.build.architecture=cortex-m7 +giga.compiler.zephyr.arch.define=-DCORE_CM7 + +giga.build.float-abi=-mfloat-abi=softfp +giga.build.extra_flags= +giga.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +giga.build.architecture=cortex-m7 +giga.build.board=ARDUINO_GIGA +giga.compiler.zephyr= +giga.vid.0=0x2341 +giga.pid.0=0x0066 +giga.upload_port.0.vid=0x2341 +giga.upload_port.0.pid=0x0066 + +giga.upload.tool=dfu-util +giga.upload.tool.default=dfu-util +giga.upload.protocol= +giga.upload.transport= +giga.upload.vid=0x2341 +giga.upload.pid=0x0366 +giga.upload.interface=0 +giga.upload.use_1200bps_touch=true +giga.upload.wait_for_upload_port=true +giga.upload.native_usb=true +giga.upload.maximum_size=1966080 +giga.upload.maximum_data_size=523624 + +giga.upload.address=0x080E0000 + +giga.upload.maximum_size=786432 +giga.upload.maximum_data_size=523624 + +giga.bootloader.tool=dfu-util +giga.bootloader.tool.default=dfu-util +giga.bootloader.vid=0x2341 +giga.bootloader.pid=0x0366 +giga.bootloader.interface=0 +giga.bootloader.file=zephyr-{build.variant}.bin +giga.bootloader.address=0x08040000 + +giga.debug.tool=gdb +giga.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +giga.debug.server.openocd.scripts.1={programmer.transport_script} +giga.debug.server.openocd.scripts.2=target/stm32h7x_dual_bank.cfg +giga.debug.cortex-debug.custom.request=attach +giga.debug.svd_file={runtime.platform.path}/svd/STM32H747_CM7.svd + +########################################################################################## + +nano33ble.name=Arduino Nano 33 BLE +nano33ble.build.core=arduino +nano33ble.build.crossprefix=arm-zephyr-eabi- +nano33ble.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +nano33ble.menu.debug.false=Standard +nano33ble.menu.debug.true=Debug + +nano33ble.menu.debug.false.postbuild_debug= +nano33ble.menu.debug.true.postbuild_debug=-debug + +nano33ble.build.zephyr_target=arduino_nano_33_ble//sense +nano33ble.build.zephyr_args= +nano33ble.build.zephyr_hals=hal_nordic +nano33ble.build.variant=arduino_nano_33_ble_nrf52840_sense +nano33ble.build.mcu=cortex-m4 +nano33ble.build.fpu=-mfpu=fpv4-sp-d16 +nano33ble.build.architecture=cortex-m4 +nano33ble.compiler.zephyr.arch.define= + +nano33ble.build.float-abi=-mfloat-abi=hard +nano33ble.build.extra_flags= +nano33ble.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +nano33ble.build.architecture=cortex-m4 +nano33ble.build.board=ARDUINO_NANO33BLE +nano33ble.compiler.zephyr= +nano33ble.vid.0=0x2341 +nano33ble.pid.0=0x035a +nano33ble.upload_port.0.vid=0x2341 +nano33ble.upload_port.0.pid=0x005a + +nano33ble.upload.tool=bossac +nano33ble.upload.tool.default=bossac +nano33ble.upload.protocol= +nano33ble.upload.transport= +nano33ble.upload.vid=0x2341 +nano33ble.upload.pid=0x005a +nano33ble.upload.interface=0 +nano33ble.upload.use_1200bps_touch=true +nano33ble.upload.wait_for_upload_port=true +nano33ble.upload.native_usb=true +nano33ble.upload.maximum_size=1966080 +nano33ble.upload.maximum_data_size=523624 +nano33ble.upload.address=0xD0000 + +nano33ble.upload.maximum_size=786432 +nano33ble.upload.maximum_data_size=523624 + +nano33ble.bootloader.tool=bossac +nano33ble.bootloader.tool.default=bossac +nano33ble.bootloader.vid=0x2341 +nano33ble.bootloader.pid=0x035b +nano33ble.bootloader.interface=0 +nano33ble.bootloader.file=zephyr-{build.variant}.bin +nano33ble.bootloader.address=0x0000 + +nano33ble.debug.tool=gdb +nano33ble.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +nano33ble.debug.server.openocd.scripts.1={programmer.transport_script} +nano33ble.debug.server.openocd.scripts.2=target/nrf52.cfg +nano33ble.debug.cortex-debug.custom.request=attach + +############################################################################################################## + +ek_ra8d1.name=EK_RA8D1 +ek_ra8d1.build.core=arduino +ek_ra8d1.build.crossprefix=arm-zephyr-eabi- +ek_ra8d1.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +ek_ra8d1.menu.debug.false=Standard +ek_ra8d1.menu.debug.true=Debug + +ek_ra8d1.menu.debug.false.postbuild_debug= +ek_ra8d1.menu.debug.true.postbuild_debug=-debug + +ek_ra8d1.build.zephyr_target=ek_ra8d1 +ek_ra8d1.build.zephyr_args= +ek_ra8d1.build.zephyr_hals=hal_renesas +ek_ra8d1.build.variant=ek_ra8d1_r7fa8d1bhecbd +ek_ra8d1.build.mcu=cortex-m85+nomve +ek_ra8d1.build.fpu=-mfpu=fpv5-d16 +ek_ra8d1.build.architecture=cortex-m85+nomve +ek_ra8d1.compiler.zephyr.arch.define= + +ek_ra8d1.build.float-abi=-mfloat-abi=hard +ek_ra8d1.build.extra_flags= +ek_ra8d1.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +ek_ra8d1.build.architecture=cortex-m85+nomve +ek_ra8d1.build.board=EK_RA8D1 +ek_ra8d1.compiler.zephyr.arch.define= + +#ek_ra8d1.recipe.hooks.objcopy.postobjcopy.4.pattern=cp {build.variant.path}/flasher.jlink "{build.path}/flasher.jlink" +#ek_ra8d1.recipe.hooks.objcopy.postobjcopy.5.pattern=sed -i 's|SKETCH|"{build.path}/{build.project_name}.llext.dfu.bin"|g' "{build.path}/flasher.jlink" + +ek_ra8d1.compiler.zephyr= +ek_ra8d1.vid.0=0x2341 +ek_ra8d1.pid.0=0x0077 +ek_ra8d1.upload_port.0.vid=0x2341 +ek_ra8d1.upload_port.0.pid=0x0077 + +ek_ra8d1.upload.tool=pyocd +ek_ra8d1.upload.tool.default=pyocd +ek_ra8d1.upload.protocol= +ek_ra8d1.upload.transport= +ek_ra8d1.upload.vid=0x2341 +ek_ra8d1.upload.pid=0x0366 +ek_ra8d1.upload.interface=0 +ek_ra8d1.upload.use_1200bps_touch=false +ek_ra8d1.upload.wait_for_upload_port=false +ek_ra8d1.upload.native_usb=true +ek_ra8d1.upload.maximum_size=1966080 +ek_ra8d1.upload.maximum_data_size=523624 +ek_ra8d1.upload.address=0x2032000 +ek_ra8d1.upload.target=R7FA8D1AH + +ek_ra8d1.bootloader.tool=pyocd +ek_ra8d1.bootloader.tool.default=pyocd +ek_ra8d1.bootloader.file=zephyr-{build.variant}.elf +ek_ra8d1.bootloader.target=R7FA8D1AH + + +############################################################################################################## + +frdm_mcxn947.name=MCXN947 +frdm_mcxn947.build.core=arduino +frdm_mcxn947.build.crossprefix=arm-zephyr-eabi- +frdm_mcxn947.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +frdm_mcxn947.menu.debug.false=Standard +frdm_mcxn947.menu.debug.true=Debug + +frdm_mcxn947.menu.debug.false.postbuild_debug= +frdm_mcxn947.menu.debug.true.postbuild_debug=-debug + +frdm_mcxn947.build.zephyr_target=frdm_mcxn947//cpu0 +frdm_mcxn947.build.zephyr_args= +frdm_mcxn947.build.zephyr_hals=hal_nxp +frdm_mcxn947.build.variant=frdm_mcxn947_mcxn947_cpu0 +frdm_mcxn947.build.mcu=cortex-m33 +frdm_mcxn947.build.fpu=-mfpu=fpv5-sp-d16 +frdm_mcxn947.build.architecture=cortex-m33 +frdm_mcxn947.compiler.zephyr.arch.define= + +frdm_mcxn947.build.float-abi=-mfloat-abi=hard +frdm_mcxn947.build.extra_flags= +frdm_mcxn947.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +frdm_mcxn947.build.board=FRDM_MCXN947 +frdm_mcxn947.compiler.zephyr.arch.define= +frdm_mcxn947.compiler.zephyr= +frdm_mcxn947.vid.0=0x1fc9 +frdm_mcxn947.pid.0=0x0143 +frdm_mcxn947.upload_port.0.vid=0x1fc9 +frdm_mcxn947.upload_port.0.pid=0x0143 +frdm_mcxn947.upload.address=0x100F0000 +frdm_mcxn947.upload.target=mcxn947vdf + +frdm_mcxn947.upload.tool=pyocd +frdm_mcxn947.upload.tool.default=pyocd +frdm_mcxn947.upload.protocol= +frdm_mcxn947.upload.transport= +frdm_mcxn947.upload.vid=0x1fc9 +frdm_mcxn947.upload.pid=0x0143 +frdm_mcxn947.upload.interface=0 +frdm_mcxn947.upload.use_1200bps_touch=false +frdm_mcxn947.upload.wait_for_upload_port=false +frdm_mcxn947.upload.native_usb=true +frdm_mcxn947.upload.maximum_size=1966080 +frdm_mcxn947.upload.maximum_data_size=523624 + +frdm_mcxn947.bootloader.tool=pyocd +frdm_mcxn947.bootloader.tool.default=pyocd +frdm_mcxn947.bootloader.file=zephyr-{build.variant}.elf +frdm_mcxn947.bootloader.target=mcxn947vdf + + +########################################################################################## + +portentah7.name=Arduino Portenta H7 +portentah7.build.core=arduino +portentah7.build.crossprefix=arm-zephyr-eabi- +portentah7.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +portentah7.menu.debug.false=Standard +portentah7.menu.debug.true=Debug + +portentah7.menu.debug.false.postbuild_debug= +portentah7.menu.debug.true.postbuild_debug=-debug + +portentah7.build.zephyr_target=arduino_portenta_h7@1.0.0//m7 +portentah7.build.zephyr_args= +portentah7.build.zephyr_hals=hal_stm32 +portentah7.build.variant=arduino_portenta_h7_stm32h747xx_m7 +portentah7.build.mcu=cortex-m7 +portentah7.build.fpu=-mfpu=fpv5-d16 +portentah7.build.architecture=cortex-m7 +portentah7.compiler.zephyr.arch.define=-DCORE_CM7 + +portentah7.build.float-abi=-mfloat-abi=softfp +portentah7.build.extra_flags= +portentah7.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +portentah7.build.architecture=cortex-m7 +portentah7.build.board=ARDUINO_PORTENTA_H7 +portentah7.compiler.zephyr= +portentah7.vid.0=0x2341 +portentah7.pid.0=0x005b +portentah7.upload_port.0.vid=0x2341 +portentah7.upload_port.0.pid=0x035b + +portentah7.upload.tool=dfu-util +portentah7.upload.tool.default=dfu-util +portentah7.upload.protocol= +portentah7.upload.transport= +portentah7.upload.vid=0x2341 +portentah7.upload.pid=0x035b +portentah7.upload.interface=0 +portentah7.upload.use_1200bps_touch=true +portentah7.upload.wait_for_upload_port=true +portentah7.upload.native_usb=true +portentah7.upload.maximum_size=1966080 +portentah7.upload.maximum_data_size=523624 + +portentah7.upload.address=0x080E0000 + +portentah7.upload.maximum_size=786432 +portentah7.upload.maximum_data_size=523624 + +portentah7.bootloader.tool=dfu-util +portentah7.bootloader.tool.default=dfu-util +portentah7.bootloader.vid=0x2341 +portentah7.bootloader.pid=0x035b +portentah7.bootloader.interface=0 +portentah7.bootloader.file=zephyr-{build.variant}.bin +portentah7.bootloader.address=0x08040000 + +portentah7.debug.tool=gdb +portentah7.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +portentah7.debug.server.openocd.scripts.1={programmer.transport_script} +portentah7.debug.server.openocd.scripts.2=target/stm32h7x_dual_bank.cfg +portentah7.debug.cortex-debug.custom.request=attach +portentah7.debug.svd_file={runtime.platform.path}/svd/STM32H747_CM7.svd + +############################################################################################################## + +frdm_rw612.name=RW612 +frdm_rw612.build.core=arduino +frdm_rw612.build.crossprefix=arm-zephyr-eabi- +frdm_rw612.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +frdm_rw612.menu.debug.false=Standard +frdm_rw612.menu.debug.true=Debug + +frdm_rw612.menu.debug.false.postbuild_debug= +frdm_rw612.menu.debug.true.postbuild_debug=-debug + +frdm_rw612.build.zephyr_target=frdm_rw612 +frdm_rw612.build.zephyr_args= +frdm_rw612.build.zephyr_hals=hal_stm32 +frdm_rw612.build.variant=frdm_rw612_rw612 +frdm_rw612.build.mcu=cortex-m33+nodsp +frdm_rw612.build.fpu=-mfpu=fpv5-sp-d16 +frdm_rw612.build.architecture=cortex-m33+nodsp +frdm_rw612.compiler.zephyr.arch.define= + +frdm_rw612.build.float-abi=-mfloat-abi=hard +frdm_rw612.build.extra_flags= +frdm_rw612.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +frdm_rw612.build.board=FRDM_RW612 +frdm_rw612.compiler.zephyr.arch.define= +frdm_rw612.compiler.zephyr= +frdm_rw612.vid.0=0x1fc9 +frdm_rw612.pid.0=0x0143 +frdm_rw612.upload_port.0.vid=0x1fc9 +frdm_rw612.upload_port.0.pid=0x0143 +frdm_rw612.upload.address=0x18323000 +frdm_rw612.upload.target=rw612 + +frdm_rw612.upload.tool=pyocd +frdm_rw612.upload.tool.default=pyocd +frdm_rw612.upload.protocol= +frdm_rw612.upload.transport= +frdm_rw612.upload.vid=0x1fc9 +frdm_rw612.upload.pid=0x0143 +frdm_rw612.upload.interface=0 +frdm_rw612.upload.use_1200bps_touch=false +frdm_rw612.upload.wait_for_upload_port=false +frdm_rw612.upload.native_usb=true +frdm_rw612.upload.maximum_size=1966080 +frdm_rw612.upload.maximum_data_size=523624 + +frdm_rw612.bootloader.tool=pyocd +frdm_rw612.bootloader.tool.default=pyocd +frdm_rw612.bootloader.file=zephyr-{build.variant}.elf +frdm_rw612.bootloader.target=rw612 + +########################################################################################## + +niclasense.name=Arduino Nicla Sense ME +niclasense.build.core=arduino +niclasense.build.crossprefix=arm-zephyr-eabi- +niclasense.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +niclasense.menu.debug.false=Standard +niclasense.menu.debug.true=Debug + +niclasense.menu.debug.false.postbuild_debug= +niclasense.menu.debug.true.postbuild_debug=-debug + +niclasense.build.zephyr_target=arduino_nicla_sense_me +niclasense.build.zephyr_args= +niclasense.build.zephyr_hals=hal_nordic +niclasense.build.variant=arduino_nicla_sense_me_nrf52832 +niclasense.build.mcu=cortex-m4 +niclasense.build.fpu=-mfpu=fpv4-sp-d16 +niclasense.build.architecture=cortex-m4 +niclasense.compiler.zephyr.arch.define= + +niclasense.build.float-abi=-mfloat-abi=hard +niclasense.build.extra_flags= +niclasense.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +niclasense.build.architecture=cortex-m4 +niclasense.build.board=ARDUINO_NICLA_SENSE_ME +niclasense.compiler.zephyr= +niclasense.vid.0=0x2341 +niclasense.pid.0=0x0360 +niclasense.upload_port.0.vid=0x2341 +niclasense.upload_port.0.pid=0x0060 + +#niclasense.upload.tool=openocd +#niclasense.upload.tool.default=openocd +niclasense.upload.tool=pyocd +niclasense.upload.tool.default=pyocd +niclasense.upload.protocol= +niclasense.upload.transport= +niclasense.upload.vid=0x2341 +niclasense.upload.pid=0x0060 +niclasense.upload.interface=0 +niclasense.upload.use_1200bps_touch=false +niclasense.upload.wait_for_upload_port=false +niclasense.upload.native_usb=false +niclasense.upload.maximum_size=1966080 +niclasense.upload.maximum_data_size=523624 +niclasense.upload.address=0x70000 +niclasense.upload.target=nrf52 + +niclasense.upload.maximum_size=786432 +niclasense.upload.maximum_data_size=523624 + +#niclasense.bootloader.tool=openocd +#niclasense.bootloader.tool.default=openocd +niclasense.bootloader.tool=pyocd +niclasense.bootloader.tool.default=pyocd +niclasense.bootloader.vid=0x2341 +niclasense.bootloader.pid=0x0360 +niclasense.bootloader.interface=0 +niclasense.bootloader.file=zephyr-{build.variant}.hex +niclasense.bootloader.target=nrf52 + +niclasense.debug.tool=gdb +niclasense.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +niclasense.debug.server.openocd.scripts.1={programmer.transport_script} +niclasense.debug.server.openocd.scripts.2=target/nrf52.cfg +niclasense.debug.cortex-debug.custom.request=attach + + +########################################################################################## + +portentac33.name=Portenta C33 +portentac33.build.core=arduino +portentac33.build.crossprefix=arm-zephyr-eabi- +portentac33.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +portentac33.menu.debug.false=Standard +portentac33.menu.debug.true=Debug + +portentac33.menu.debug.false.postbuild_debug= +portentac33.menu.debug.true.postbuild_debug=-debug + +portentac33.menu.mode.llext=llext +portentac33.menu.mode.linked=linked + +portentac33.menu.mode.linked.build.extra_ldflags=-lc -lm -lgcc -L{build.variant.path} -Wl,--wrap=random -Wl,--wrap=calloc -Wl,--wrap=free -Wl,--wrap=malloc -Wl,--wrap=realloc +portentac33.menu.mode.linked.build.llext_link_flags= +portentac33.menu.mode.linked.build.suffix=_linked +portentac33.menu.mode.linked.build.ldscript={runtime.platform.path}/variants/_linked/linker_script.ld +portentac33.menu.mode.linked.upload.extension=bin-zsk.bin +portentac33.menu.mode.linked.postbuild_mode=-prelinked + +portentac33.build.zephyr_target=arduino_portenta_c33 +portentac33.build.zephyr_args= +portentac33.build.zephyr_hals=hal_renesas +portentac33.build.variant=arduino_portenta_c33_r7fa6m5bh3cfc +portentac33.build.mcu=cortex-m33 +portentac33.build.fpu=-mfpu=fpv5-sp-d16 +portentac33.build.architecture=cortex-m33 +portentac33.compiler.zephyr.arch.define= + +portentac33.build.float-abi=-mfloat-abi=hard +portentac33.build.extra_flags= +portentac33.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +portentac33.build.board=ARDUINO_PORTENTA_C33 +portentac33.compiler.zephyr.arch.define= +portentac33.compiler.zephyr= +portentac33.vid.0=0x2341 +portentac33.pid.0=0x0068 +portentac33.upload_port.0.vid=0x2341 +portentac33.upload_port.0.pid=0x0068 +portentac33.upload.target=portentac33 + +portentac33.upload.tool=dfu-util +portentac33.upload.tool.default=dfu-util +portentac33.upload.protocol= +portentac33.upload.transport= +portentac33.upload.vid=0x2341 +portentac33.upload.pid=0x0368 +portentac33.upload.interface=1 +portentac33.upload.use_1200bps_touch=true +portentac33.upload.wait_for_upload_port=false +portentac33.upload.native_usb=true +portentac33.upload.maximum_size=1966080 +portentac33.upload.maximum_data_size=523624 +portentac33.upload.address=0x100000 +portentac33.upload.dfuse=-Q + +portentac33.bootloader.tool=dfu-util +portentac33.bootloader.tool.default=dfu-util +portentac33.bootloader.file=zephyr-{build.variant}.bin +portentac33.bootloader.interface=0 +portentac33.bootloader.address=0x10000 +portentac33.bootloader.dfuse=-Q + +########################################################################################## + +opta.name=Arduino Opta +opta.build.core=arduino +opta.build.crossprefix=arm-zephyr-eabi- +opta.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +opta.menu.debug.false=Standard +opta.menu.debug.true=Debug + +opta.menu.debug.false.postbuild_debug= +opta.menu.debug.true.postbuild_debug=-debug + +opta.build.zephyr_target=arduino_opta//m7 +opta.build.zephyr_args= +opta.build.zephyr_hals=hal_stm32 +opta.build.variant=arduino_opta_stm32h747xx_m7 +opta.build.mcu=cortex-m7 +opta.build.fpu=-mfpu=fpv5-d16 +opta.build.architecture=cortex-m7 +opta.compiler.zephyr.arch.define=-DCORE_CM7 + +opta.build.float-abi=-mfloat-abi=softfp +opta.build.extra_flags= +opta.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +opta.build.architecture=cortex-m7 +opta.build.board=ARDUINO_OPTA +opta.compiler.zephyr= +opta.vid.0=0x2341 +opta.pid.0=0x0064 +opta.upload_port.0.vid=0x2341 +opta.upload_port.0.pid=0x0364 + +opta.upload.tool=dfu-util +opta.upload.tool.default=dfu-util +opta.upload.protocol= +opta.upload.transport= +opta.upload.vid=0x2341 +opta.upload.pid=0x0364 +opta.upload.interface=0 +opta.upload.use_1200bps_touch=true +opta.upload.wait_for_upload_port=true +opta.upload.native_usb=true +opta.upload.maximum_size=1966080 +opta.upload.maximum_data_size=523624 + +opta.upload.address=0x080E0000 + +opta.upload.maximum_size=786432 +opta.upload.maximum_data_size=523624 + +opta.bootloader.tool=dfu-util +opta.bootloader.tool.default=dfu-util +opta.bootloader.vid=0x2341 +opta.bootloader.pid=0x0364 +opta.bootloader.interface=0 +opta.bootloader.file=zephyr-{build.variant}.bin +opta.bootloader.address=0x08040000 + +opta.debug.tool=gdb +opta.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +opta.debug.server.openocd.scripts.1={programmer.transport_script} +opta.debug.server.openocd.scripts.2=target/stm32h7x_dual_bank.cfg +opta.debug.cortex-debug.custom.request=attach +opta.debug.svd_file={runtime.platform.path}/svd/STM32H747_CM7.svd + +############################################################################################################## diff --git a/cores/CMakeLists.txt b/cores/CMakeLists.txt index f7b781c9..3ce59d21 100644 --- a/cores/CMakeLists.txt +++ b/cores/CMakeLists.txt @@ -1,3 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 -add_subdirectory(arduino) \ No newline at end of file +add_subdirectory(arduino) + +zephyr_include_directories(../../ArduinoCore-API/) + +if(NOT DEFINED ARDUINO_BUILD_PATH) +zephyr_sources(../../ArduinoCore-API/api/CanMsg.cpp) +zephyr_sources(../../ArduinoCore-API/api/CanMsgRingbuffer.cpp) +zephyr_sources(../../ArduinoCore-API/api/Common.cpp) +zephyr_sources(../../ArduinoCore-API/api/IPAddress.cpp) +zephyr_sources(../../ArduinoCore-API/api/Print.cpp) +zephyr_sources(../../ArduinoCore-API/api/Stream.cpp) +zephyr_sources(../../ArduinoCore-API/api/String.cpp) +endif() diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index e9399e72..0d07764c 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -12,13 +12,16 @@ #include #include #include +#include #include +#include +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 +/* Note: DT_REG_ADDR needs an expanded argument or it will not work properly */ +#define DIGITAL_PIN_MATCHES(dev_pha, pin, dev, num) \ + (((dev == DT_REG_ADDR(dev_pha)) && (num == pin)) ? 1 : 0) #define DIGITAL_PIN_EXISTS(n, p, i, dev, num) \ - (((dev == DT_REG_ADDR(DT_PHANDLE_BY_IDX(n, p, i))) && \ - (num == DT_PHA_BY_IDX(n, p, i, pin))) \ - ? 1 \ - : 0) + DIGITAL_PIN_MATCHES(DT_PHANDLE_BY_IDX(n, p, i), DT_PHA_BY_IDX(n, p, i, pin), dev, num) /* Check all pins are defined only once */ #define DIGITAL_PIN_CHECK_UNIQUE(i, _) \ @@ -32,6 +35,7 @@ #endif #undef DIGITAL_PIN_CHECK_UNIQUE +#endif #ifndef LED_BUILTIN @@ -83,7 +87,9 @@ * enum digitalPins { D0, D1, ... LED... NUM_OF_DIGITAL_PINS }; */ enum digitalPins { +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, DN_ENUMS, (, )), +#endif NUM_OF_DIGITAL_PINS }; @@ -95,6 +101,20 @@ enum digitalPins { enum analogPins { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, AN_ENUMS) }; +// We provide analogReadResolution APIs +void analogReadResolution(int bits); + +#endif + +#ifdef CONFIG_DAC + +#undef DAC0 +#undef DAC1 +#undef DAC2 +#undef DAC3 +#define DAC_ENUMS(n, p, i) DAC ## i = i, +enum dacPins { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_ENUMS) NUM_OF_DACS }; + #endif void interrupts(void); @@ -102,8 +122,24 @@ void noInterrupts(void); int digitalPinToInterrupt(pin_size_t pin); +#define digitalPinToPort(x) (x) +#define digitalPinToBitMask(x) (x) +#define portOutputRegister(x) (x) +#define portInputRegister(x) (x) + +void analogReadResolution(int bits); +void analogWriteResolution(int bits); + #include #ifdef __cplusplus -#include +#include #include +#include +#include +#include +#include + +// Allow namespace-less operations if Arduino.h is included +using namespace arduino; + #endif // __cplusplus diff --git a/cores/arduino/CMakeLists.txt b/cores/arduino/CMakeLists.txt index 193fd3c1..d4811041 100644 --- a/cores/arduino/CMakeLists.txt +++ b/cores/arduino/CMakeLists.txt @@ -4,12 +4,14 @@ zephyr_include_directories(../../variants) if(NOT DEFINED ARDUINO_BUILD_PATH) -zephyr_sources(zephyrPrint.cpp) zephyr_sources(zephyrSerial.cpp) zephyr_sources(zephyrCommon.cpp) +zephyr_sources(USB.cpp) +zephyr_sources(itoa.cpp) if(DEFINED CONFIG_ARDUINO_ENTRY) zephyr_sources(main.cpp) +zephyr_sources(threads.cpp) endif() endif() diff --git a/cores/arduino/SerialUSB.h b/cores/arduino/SerialUSB.h new file mode 100644 index 00000000..442d28d0 --- /dev/null +++ b/cores/arduino/SerialUSB.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace arduino { + +class SerialUSB_ : public ZephyrSerial { + +public: + SerialUSB_(const struct device *dev) : ZephyrSerial(dev) { } + void begin(unsigned long baudrate, uint16_t config); + void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); } + + operator bool() override; + size_t write(const uint8_t *buffer, size_t size) override; + void flush() override; + +protected: + uint32_t dtr = 0; + uint32_t baudrate; + void _baudChangeHandler(); + static void _baudChangeDispatch(struct k_timer *timer); + +private: + struct k_timer baud_timer; + bool started = false; +}; +} // namespace arduino + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +extern arduino::SerialUSB_ Serial; +#endif \ No newline at end of file diff --git a/cores/arduino/USB.cpp b/cores/arduino/USB.cpp new file mode 100644 index 00000000..60f5fc7a --- /dev/null +++ b/cores/arduino/USB.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#if ((DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm)) && (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +const struct device *const usb_dev = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), cdc_acm, 0)); + +void usb_status_cb(enum usb_dc_status_code cb_status, const uint8_t *param) { + (void)param; // unused + if (cb_status == USB_DC_CONFIGURED) { + + } +} + +void __attribute__((weak)) _on_1200_bps() { + NVIC_SystemReset(); +} + +void arduino::SerialUSB_::_baudChangeHandler() +{ + uart_line_ctrl_get(uart, UART_LINE_CTRL_BAUD_RATE, &baudrate); + if (baudrate == 1200) { + usb_disable(); + _on_1200_bps(); + } +} + +static void _baudChangeHandler(const struct device *dev, uint32_t rate) +{ + (void)dev; // unused + if (rate == 1200) { + usb_disable(); + _on_1200_bps(); + } +} + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) + +extern "C" { + #include + struct usbd_context *usbd_init_device(usbd_msg_cb_t msg_cb); +} + +struct usbd_context *_usbd; + +int usb_disable() { + return usbd_disable(_usbd); +} + +static void usbd_next_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) +{ + if (usbd_can_detect_vbus(ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + usbd_enable(ctx); + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + usbd_disable(ctx); + } + } + + if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) { + uint32_t baudrate; + uart_line_ctrl_get(ctx->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); + _baudChangeHandler(nullptr, baudrate); + } +} + +static int enable_usb_device_next(void) +{ + int err; + + //_usbd = usbd_init_device(usbd_next_cb); + _usbd = usbd_init_device(nullptr); + if (_usbd == NULL) { + return -ENODEV; + } + + if (!usbd_can_detect_vbus(_usbd)) { + err = usbd_enable(_usbd); + if (err) { + return err; + } + } + return 0; +} +#endif /* defined(CONFIG_USB_DEVICE_STACK_NEXT) */ + +void arduino::SerialUSB_::_baudChangeDispatch(struct k_timer *timer) { + arduino::SerialUSB_* dev = (arduino::SerialUSB_*)k_timer_user_data_get(timer); + dev->_baudChangeHandler(); +} + + +void arduino::SerialUSB_::begin(unsigned long baudrate, uint16_t config) { + if (!started) { + #ifndef CONFIG_USB_DEVICE_STACK_NEXT + usb_enable(NULL); + #ifndef CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT + k_timer_init(&baud_timer, SerialUSB_::_baudChangeDispatch, NULL); + k_timer_user_data_set(&baud_timer, this); + k_timer_start(&baud_timer, K_MSEC(100), K_MSEC(100)); + #else + cdc_acm_dte_rate_callback_set(usb_dev, ::_baudChangeHandler); + #endif + #else + enable_usb_device_next(); + #endif + ZephyrSerial::begin(baudrate, config); + started = true; + } +} + +arduino::SerialUSB_::operator bool() { + uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr); + return dtr; +} + + +size_t arduino::SerialUSB_::write(const uint8_t *buffer, size_t size) { + if (!Serial) return 0; + return arduino::ZephyrSerial::write(buffer, size); +} + +void arduino::SerialUSB_::flush() { + if (!Serial) return; + arduino::ZephyrSerial::flush(); +} + +arduino::SerialUSB_ Serial(usb_dev); +#endif diff --git a/cores/arduino/abi.cpp b/cores/arduino/abi.cpp new file mode 100644 index 00000000..5c030cd3 --- /dev/null +++ b/cores/arduino/abi.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +extern "C" void __cxa_pure_virtual(void) {} +extern "C" void __cxa_deleted_virtual(void) {} +extern "C" int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle) { + (void)func; (void)arg; (void)dso_handle; // unused + return 0; +} + +namespace std { + void __throw_length_error(const char* __s __attribute__((unused))) {} +}; + +extern "C" int strcmp(const char* s1, const char* s2) { + while(*s1 && (*s1 == *s2)) + { + s1++; + s2++; + } + return *(const unsigned char*)s1 - *(const unsigned char*)s2; +} diff --git a/cores/arduino/api b/cores/arduino/api new file mode 120000 index 00000000..d32ec695 --- /dev/null +++ b/cores/arduino/api @@ -0,0 +1 @@ +../../../modules/lib/ArduinoCore-API/api/ \ No newline at end of file diff --git a/cores/arduino/itoa.cpp b/cores/arduino/itoa.cpp new file mode 100644 index 00000000..929ec4d0 --- /dev/null +++ b/cores/arduino/itoa.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +extern char* itoa( int value, char *string, int radix ) +{ + return ltoa( value, string, radix ) ; +} + +extern char* ltoa( long value, char *string, int radix ) +{ + char tmp[33]; + char *tp = tmp; + long i; + unsigned long v; + int sign; + char *sp; + + if ( string == NULL ) + { + return 0 ; + } + + if (radix > 36 || radix <= 1) + { + return 0 ; + } + + sign = (radix == 10 && value < 0); + if (sign) + { + v = -value; + } + else + { + v = (unsigned long)value; + } + + while (v || tp == tmp) + { + i = v % radix; + v = v / radix; + if (i < 10) + *tp++ = i+'0'; + else + *tp++ = i + 'a' - 10; + } + + sp = string; + + if (sign) + *sp++ = '-'; + while (tp > tmp) + *sp++ = *--tp; + *sp = 0; + + return string; +} + +extern char* utoa( unsigned int value, char *string, int radix ) +{ + return ultoa( value, string, radix ) ; +} + +extern char* ultoa( unsigned long value, char *string, int radix ) +{ + char tmp[33]; + char *tp = tmp; + long i; + unsigned long v = value; + char *sp; + + if ( string == NULL ) + { + return 0; + } + + if (radix > 36 || radix <= 1) + { + return 0; + } + + while (v || tp == tmp) + { + i = v % radix; + v = v / radix; + if (i < 10) + *tp++ = i+'0'; + else + *tp++ = i + 'a' - 10; + } + + sp = string; + + + while (tp > tmp) + *sp++ = *--tp; + *sp = 0; + + return string; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 97afd420..9359e745 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -5,14 +5,94 @@ */ #include "Arduino.h" +#include "zephyr/kernel.h" +#include +#ifdef CONFIG_LLEXT +#include +#endif + +#ifdef CONFIG_MULTITHREADING +void start_static_threads(); +#endif + + +// This function will be overwriten by most variants. +void __attribute__((weak))initVariant(void) { + +} + int main(void) { +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && CONFIG_USB_CDC_ACM) + Serial.begin(115200); +#endif + + initVariant(); + +#ifdef CONFIG_MULTITHREADING + start_static_threads(); +#endif + setup(); for (;;) { loop(); + #if 0 //(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && CONFIG_USB_CDC_ACM) || DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) if (arduino::serialEventRun) arduino::serialEventRun(); + #endif } return 0; } + +#ifdef CONFIG_LLEXT +LL_EXTENSION_SYMBOL(main); +#endif + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start []) (void) __attribute__((weak)); +extern void (*__preinit_array_end []) (void) __attribute__((weak)); +extern void (*__init_array_start []) (void) __attribute__((weak)); +extern void (*__init_array_end []) (void) __attribute__((weak)); + +static void __libc_init_array (void) +{ + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) + __preinit_array_start[i] (); + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) + __init_array_start[i] (); +} + +extern "C" __attribute__((section(".entry_point"), used)) void entry_point(struct k_heap* stack, size_t stack_size) { + (void)stack; (void)stack_size; // unused + + // copy .data in the right place + // .bss should already be in the right place + // call constructors + extern uintptr_t _sidata; + extern uintptr_t _sdata; + extern uintptr_t _edata; + extern uintptr_t _sbss; + extern uintptr_t _ebss; + extern uintptr_t __heap_start; + extern uintptr_t __heap_end; + extern uintptr_t kheap_llext_heap; + extern uintptr_t kheap_llext_heap_size; + + //__asm volatile ("cpsie i"); + + printk("System Heap end: %p\n", &__heap_end); + printk("System Heap start: %p\n", &__heap_start); + printk("Sketch Heap start: %p, size %p\n", &kheap_llext_heap, &kheap_llext_heap_size); + + memcpy(&_sdata, &_sidata, (&_edata - &_sdata) * sizeof(uint32_t)); + memset(&_sbss, 0, &_ebss - &_sbss); + __libc_init_array(); + main(); +} diff --git a/cores/arduino/new b/cores/arduino/new new file mode 100644 index 00000000..b97bc949 --- /dev/null +++ b/cores/arduino/new @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef NEW_H +#define NEW_H + +#include +//#pragma long_calls + +namespace std { + struct nothrow_t {}; + extern const nothrow_t nothrow; + + // These are not actually implemented, to prevent overhead and + // complexity. They are still declared to allow implementing + // them in user code if needed. + typedef void (*new_handler)(); + new_handler set_new_handler(new_handler new_p) noexcept; + new_handler get_new_handler() noexcept; + + // This is normally declared in various headers that we do not have + // available, so just define it here. We could also use ::size_t + // below, but then anyone including can no longer assume + // std::size_t is available. + using size_t = ::size_t; +} // namespace std + +void * operator new(std::size_t size); +void * operator new[](std::size_t size); + +#if __cplusplus >= 201703L +void * operator new(std::size_t count, std::align_val_t al); +void * operator new[](std::size_t count, std::align_val_t al); + +void* operator new(std::size_t count, std::align_val_t al, + const std::nothrow_t& tag) noexcept; +void* operator new[](std::size_t count, std::align_val_t al, + const std::nothrow_t& tag) noexcept; +#endif + +void * operator new(std::size_t size, const std::nothrow_t tag) noexcept; +void * operator new[](std::size_t size, const std::nothrow_t& tag) noexcept; + +void * operator new(std::size_t size, void *place) noexcept; +void * operator new[](std::size_t size, void *place) noexcept; + + +void operator delete(void * ptr) noexcept; +void operator delete[](void * ptr) noexcept; + +#if __cplusplus >= 201402L +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete[](void * ptr, std::size_t size) noexcept; +#endif // __cplusplus >= 201402L + +void operator delete(void* ptr, const std::nothrow_t& tag) noexcept; +void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept; + +void operator delete(void* ptr, void* place) noexcept; +void operator delete[](void* ptr, void* place) noexcept; + +#endif + diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp new file mode 100644 index 00000000..ed203f86 --- /dev/null +++ b/cores/arduino/new.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "new.h" + +// The C++ spec dictates that allocation failure should cause the +// (non-nothrow version of the) operator new to throw an exception. +// Since we expect to have exceptions disabled, it would be more +// appropriate (and probably standards-compliant) to terminate instead. +// Historically failure causes null to be returned, but this define +// allows switching to more robust terminating behaviour (that might +// become the default at some point in the future). Note that any code +// that wants null to be returned can (and should) use the nothrow +// versions of the new statement anyway and is unaffected by this. +// #define NEW_TERMINATES_ON_FAILURE + +namespace std { + // Defined in abi.cpp + void terminate(); + + const nothrow_t nothrow; +} + +static void * new_helper(std::size_t size) { + // Even zero-sized allocations should return a unique pointer, but + // malloc does not guarantee this + if (size == 0) + size = 1; + return malloc(size); +} + +void * operator new(std::size_t size) { + void *res = new_helper(size); +#if defined(NEW_TERMINATES_ON_FAILURE) + if (!res) + std::terminate(); +#endif + return res; +} +void * operator new[](std::size_t size) { + return operator new(size); +} + +#if __cplusplus >= 201703L +void* operator new(std::size_t count, std::align_val_t al) { + (void)al; // unused + return operator new(count); +} + +void* operator new[](std::size_t count, std::align_val_t al) { + (void)al; // unused + return operator new(count); +} + +void * operator new(std::size_t size, std::align_val_t al, const std::nothrow_t tag) noexcept { + (void)al; (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else + return operator new(size); +#endif +} + +void * operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t& tag) noexcept { + (void)al; (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else + return operator new[](size); +#endif +} +#endif + +void * operator new(std::size_t size, const std::nothrow_t tag) noexcept { + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else + return operator new(size); +#endif +} +void * operator new[](std::size_t size, const std::nothrow_t& tag) noexcept { + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else + return operator new[](size); +#endif +} + +void * operator new(std::size_t size, void *place) noexcept { + // Nothing to do + (void)size; // unused + return place; +} +void * operator new[](std::size_t size, void *place) noexcept { + return operator new(size, place); +} + +void operator delete(void * ptr) noexcept { + free(ptr); +} +void operator delete[](void * ptr) noexcept { + operator delete(ptr); +} + +#if __cplusplus >= 201402L +void operator delete(void* ptr, std::size_t size) noexcept { + (void)size; // unused + operator delete(ptr); +} +void operator delete[](void * ptr, std::size_t size) noexcept { + (void)size; // unused + operator delete[](ptr); +} +#endif // __cplusplus >= 201402L + +void operator delete(void* ptr, const std::nothrow_t& tag) noexcept { + (void)tag; // unused + operator delete(ptr); +} +void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept { + (void)tag; // unused + operator delete[](ptr); +} + +void operator delete(void* ptr, void* place) noexcept { + (void)ptr; (void)place; // unused + // Nothing to do +} +void operator delete[](void* ptr, void* place) noexcept { + (void)ptr; (void)place; // unused + // Nothing to do +} diff --git a/cores/arduino/new.h b/cores/arduino/new.h new file mode 100644 index 00000000..d5298536 --- /dev/null +++ b/cores/arduino/new.h @@ -0,0 +1,3 @@ +// This file originally used a non-standard name for this Arduino core +// only, so still expose the old new.h name for compatibility. +#include "new" diff --git a/cores/arduino/overloads.h b/cores/arduino/overloads.h new file mode 100644 index 00000000..5670b270 --- /dev/null +++ b/cores/arduino/overloads.h @@ -0,0 +1,9 @@ +#ifdef CONFIG_DAC + +void analogWrite(enum dacPins pinNumber, int value); + +#endif + +// In c++ mode, we also provide analogReadResolution and analogWriteResolution getters +int analogReadResolution(); +int analogWriteResolution(); \ No newline at end of file diff --git a/cores/arduino/threads.cpp b/cores/arduino/threads.cpp new file mode 100644 index 00000000..99a4a141 --- /dev/null +++ b/cores/arduino/threads.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Arduino.h" + +#ifdef CONFIG_MULTITHREADING +void start_static_threads() { + #define _FOREACH_STATIC_THREAD(thread_data) \ + STRUCT_SECTION_FOREACH(_static_thread_data, thread_data) + + _FOREACH_STATIC_THREAD(thread_data) { + k_thread_create(thread_data->init_thread, thread_data->init_stack, thread_data->init_stack_size, thread_data->init_entry, + thread_data->init_p1, thread_data->init_p2, thread_data->init_p3, thread_data->init_prio, + thread_data->init_options, thread_data->init_delay); + k_thread_name_set(thread_data->init_thread, thread_data->init_name); + thread_data->init_thread->init_data = thread_data; + } + + /* + * Take a sched lock to prevent them from running + * until they are all started. + */ + k_sched_lock(); + _FOREACH_STATIC_THREAD(thread_data) { + k_thread_start(thread_data->init_thread); + } + k_sched_unlock(); +} +#endif \ No newline at end of file diff --git a/cores/arduino/time_macros.h b/cores/arduino/time_macros.h new file mode 100644 index 00000000..6c9290f6 --- /dev/null +++ b/cores/arduino/time_macros.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define clockCyclesPerMicrosecond() (1000000 / k_cyc_to_ns_near64(1000)) +#define clockCyclesToMicroseconds(a) (a / clockCyclesPerMicrosecond()) +#define microsecondsToClockCycles(a) (a * clockCyclesPerMicrosecond()) diff --git a/cores/arduino/usb_device_descriptor.c b/cores/arduino/usb_device_descriptor.c new file mode 100644 index 00000000..1c0eb887 --- /dev/null +++ b/cores/arduino/usb_device_descriptor.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_USB_DEVICE_STACK_NEXT + +/* By default, do not register the USB DFU class DFU mode instance. */ +static const char *const blocklist[] = { + "dfu_dfu", + NULL, +}; + +/* doc device instantiation start */ +USBD_DEVICE_DEFINE(usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + CONFIG_USB_DEVICE_VID, CONFIG_USB_DEVICE_PID); +/* doc device instantiation end */ + +/* doc string instantiation start */ +USBD_DESC_LANG_DEFINE(sample_lang); +USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_USB_DEVICE_MANUFACTURER); +USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_USB_DEVICE_PRODUCT); +USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); +/* doc string instantiation end */ + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + +/* doc configuration instantiation start */ +static const uint8_t attributes = 0; + +/* Full speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_fs_config, + attributes, + 250, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_hs_config, + attributes, + 250, &hs_cfg_desc); +/* doc configuration instantiation end */ + +/* + * This does not yet provide valuable information, but rather serves as an + * example, and will be improved in the future. + */ +static const struct usb_bos_capability_lpm bos_cap_lpm = { + .bLength = sizeof(struct usb_bos_capability_lpm), + .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION, + .bmAttributes = 0UL, +}; + +USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm); + +static void sample_fix_code_triple(struct usbd_context *uds_ctx, + const enum usbd_speed speed) +{ + /* Always use class code information from Interface Descriptors */ + if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) || + IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) { + /* + * Class with multiple interfaces have an Interface + * Association Descriptor available, use an appropriate triple + * to indicate it. + */ + usbd_device_set_code_triple(uds_ctx, speed, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } else { + usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); + } +} + +struct usbd_context *usbd_setup_device(usbd_msg_cb_t msg_cb) +{ + int err; + + /* doc add string descriptor start */ + err = usbd_add_descriptor(&usbd, &sample_lang); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_mfr); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_product); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_sn); + if (err) { + return NULL; + } + /* doc add string descriptor end */ + + if (usbd_caps_speed(&usbd) == USBD_SPEED_HS) { + err = usbd_add_configuration(&usbd, USBD_SPEED_HS, + &sample_hs_config); + if (err) { + return NULL; + } + + err = usbd_register_all_classes(&usbd, USBD_SPEED_HS, 1, + blocklist); + if (err) { + return NULL; + } + + sample_fix_code_triple(&usbd, USBD_SPEED_HS); + } + + /* doc configuration register start */ + err = usbd_add_configuration(&usbd, USBD_SPEED_FS, + &sample_fs_config); + if (err) { + return NULL; + } + /* doc configuration register end */ + + /* doc functions register start */ + err = usbd_register_all_classes(&usbd, USBD_SPEED_FS, 1, blocklist); + if (err) { + return NULL; + } + /* doc functions register end */ + + sample_fix_code_triple(&usbd, USBD_SPEED_FS); + + if (msg_cb != NULL) { + /* doc device init-and-msg start */ + err = usbd_msg_register_cb(&usbd, msg_cb); + if (err) { + return NULL; + } + /* doc device init-and-msg end */ + } + + if (0) { + (void)usbd_device_set_bcd_usb(&usbd, USBD_SPEED_FS, 0x0201); + (void)usbd_device_set_bcd_usb(&usbd, USBD_SPEED_HS, 0x0201); + + err = usbd_add_descriptor(&usbd, &sample_usbext); + if (err) { + return NULL; + } + } + + return &usbd; +} + +struct usbd_context *usbd_init_device(usbd_msg_cb_t msg_cb) +{ + int err; + + if (usbd_setup_device(msg_cb) == NULL) { + return NULL; + } + + /* doc device init start */ + err = usbd_init(&usbd); + if (err) { + return NULL; + } + /* doc device init end */ + + return &usbd; +} + +#endif \ No newline at end of file diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 6fa94bc4..f0b6e8ba 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -12,41 +12,43 @@ static const struct gpio_dt_spec arduino_pins[] = {DT_FOREACH_PROP_ELEM_SEP( namespace { +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 + /* * Calculate GPIO ports/pins number statically from devicetree configuration */ -template constexpr const N sum_of_list(const N sum, const Head &head) +template constexpr N sum_of_list(const N sum, const Head &head) { return sum + head; } template -constexpr const N sum_of_list(const N sum, const Head &head, const Tail &...tail) +constexpr N sum_of_list(const N sum, const Head &head, const Tail &...tail) { return sum_of_list(sum + head, tail...); } -template constexpr const N max_in_list(const N max, const Head &head) +template constexpr N max_in_list(const N max, const Head &head) { return (max >= head) ? max : head; } template -constexpr const N max_in_list(const N max, const Head &head, const Tail &...tail) +constexpr N max_in_list(const N max, const Head &head, const Tail &...tail) { return max_in_list((max >= head) ? max : head, tail...); } template -constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, const Query &query, const Head &head) { return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : 0; } template -constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, const Query &query, const Head &head, const Tail &...tail) { @@ -68,6 +70,13 @@ const int port_num = const int max_ngpios = max_in_list( 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, GPIO_NGPIOS, (, ))); +#else + +const int port_num = 1; +const int max_ngpios = 0; + +#endif + /* * GPIO callback implementation */ @@ -110,6 +119,7 @@ void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + (void)port; // unused struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; for (uint32_t i = 0; i < max_ngpios; i++) { @@ -154,14 +164,14 @@ size_t pwm_pin_index(pin_size_t pinNumber) { DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), #define ADC_CH_CFG(n,p,i) arduino_adc[i].channel_cfg, -const struct adc_dt_spec arduino_adc[] = +static const struct adc_dt_spec arduino_adc[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC) }; /* io-channel-pins node provides a mapping digital pin numbers to adc channels */ const pin_size_t arduino_analog_pins[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ADC_PINS) }; -struct adc_channel_cfg channel_cfg[ARRAY_SIZE(arduino_analog_pins)] = +struct adc_channel_cfg channel_cfg[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG) }; size_t analog_pin_index(pin_size_t pinNumber) { @@ -175,6 +185,28 @@ size_t analog_pin_index(pin_size_t pinNumber) { #endif //CONFIG_ADC +#ifdef CONFIG_DAC + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), dac)) + +#define DAC_NODE DT_PHANDLE(DT_PATH(zephyr_user), dac) +#define DAC_RESOLUTION DT_PROP(DT_PATH(zephyr_user), dac_resolution) +static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE); + +#define DAC_CHANNEL_DEFINE(n, p, i) \ + { \ + .channel_id = DT_PROP_BY_IDX(n, p, i), \ + .resolution = DAC_RESOLUTION, \ + .buffered = true, \ + }, + +static const struct dac_channel_cfg dac_ch_cfg[] = + { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_CHANNEL_DEFINE) }; + +#endif + +#endif //CONFIG_DAC + static unsigned int irq_key; static bool interrupts_disabled = false; } @@ -262,25 +294,41 @@ void delay(unsigned long ms) { k_sleep(K_MSEC(ms)); } void delayMicroseconds(unsigned int us) { k_sleep(K_USEC(us)); } unsigned long micros(void) { +#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER + return k_cyc_to_us_floor32(k_cycle_get_64()); +#else return k_cyc_to_us_floor32(k_cycle_get_32()); -} +#endif + } unsigned long millis(void) { return k_uptime_get_32(); } +#if defined(CONFIG_DAC) || defined(CONFIG_PWM) +static int _analog_write_resolution = 8; +void analogWriteResolution(int bits) { + _analog_write_resolution = bits; +} +int analogWriteResolution() { + return _analog_write_resolution; +} +#endif + #ifdef CONFIG_PWM void analogWrite(pin_size_t pinNumber, int value) { size_t idx = pwm_pin_index(pinNumber); - if (!pwm_is_ready_dt(&arduino_pwm[idx])) { + if (idx >= ARRAY_SIZE(arduino_pwm) ) { return; } - if (idx >= ARRAY_SIZE(arduino_pwm) ) { + if (!pwm_is_ready_dt(&arduino_pwm[idx])) { return; } + value = map(value, 0, 1 << _analog_write_resolution, 0, arduino_pwm[idx].period); + if (((uint32_t)value) > arduino_pwm[idx].period) { value = arduino_pwm[idx].period; } else if (value < 0) { @@ -296,6 +344,21 @@ void analogWrite(pin_size_t pinNumber, int value) #endif +#ifdef CONFIG_DAC +void analogWrite(enum dacPins dacName, int value) +{ + if (dacName >= NUM_OF_DACS) { + return; + } + + dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); + + const int max_dac_value = 1U << dac_ch_cfg[dacName].resolution; + dac_write_value(dac_dev, dac_ch_cfg[dacName].channel_id, map(value, 0, 1 << _analog_write_resolution, 0, max_dac_value)); +} +#endif + + #ifdef CONFIG_ADC void analogReference(uint8_t mode) @@ -310,10 +373,24 @@ void analogReference(uint8_t mode) } } +// Note: We can not update the arduino_adc structure as it is read only... +static int read_resolution = 10; + +void analogReadResolution(int bits) +{ + read_resolution = bits; +} + +int analogReadResolution() +{ + return read_resolution; +} + + int analogRead(pin_size_t pinNumber) { int err; - int16_t buf; + uint16_t buf; struct adc_sequence seq = { .buffer = &buf, .buffer_size = sizeof(buf) }; size_t idx = analog_pin_index(pinNumber); @@ -343,7 +420,13 @@ int analogRead(pin_size_t pinNumber) return err; } - return buf; + /* + * If necessary map the return value to the + * number of bits the user has asked for + */ + if (read_resolution == seq.resolution) return buf; + if (read_resolution < seq.resolution) return buf >> (seq.resolution - read_resolution); + return buf << (read_resolution - seq.resolution) ; } #endif diff --git a/cores/arduino/zephyrPrint.cpp b/cores/arduino/zephyrPrint.cpp deleted file mode 100644 index 2e07c232..00000000 --- a/cores/arduino/zephyrPrint.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2022 TOKITA Hiroshi - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include -#include - -namespace arduino -{ -namespace zephyr -{ - -int cbprintf_callback(int c, void *ctx) -{ - return reinterpret_cast(ctx)->write((unsigned char)c); -} - -size_t wrap_cbprintf(void *ctx, const char *format, ...) -{ - va_list ap; - int rc; - - va_start(ap, format); - rc = cbvprintf(reinterpret_cast(cbprintf_callback), ctx, format, ap); - va_end(ap); - - return static_cast(rc > 0 ? rc : 0); -} - -size_t print_number_base_any(void *ctx, unsigned long long ull, int base) -{ - arduino::Print &print = *reinterpret_cast(ctx); - char string[sizeof(unsigned long long) * 8] = {0}; - size_t digit = 0; - unsigned value; - - if (base < 2 || base > ('~' - 'A' + 10)) { - base = 10; - } - - while (ull != 0) { - value = ull % base; - if (value < 10) { - string[sizeof(string) - digit] = '0' + value; - } else { - string[sizeof(string) - digit] = 'A' + (value- 10); - } - - digit++; - ull /= base; - } - - return print.write(string + (sizeof(string) - digit), digit + 1); -} - -size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits) -{ - arduino::Print &print = *reinterpret_cast(ctx); - const unsigned long long mask = (1 << bits) - 1; - int digit = (((sizeof(unsigned long long) * 8) + bits) / bits); - int output_count = -1; - unsigned value; - - while (digit >= 0) { - value = (ull & (mask << (digit * bits))) >> (digit * bits); - if (value != 0 && output_count < 0) { - output_count = 0; - } - - if (output_count >= 0) { - if (value < 10) { - print.write('0' + value); - } else { - print.write('A' + (value- 10)); - } - output_count++; - } - digit--; - } - - return output_count; -} - -} // namespace zephyr -} // namespace arduino - -/* - * This is the default implementation. - * It will be overridden by subclassese. - */ -size_t arduino::Print::write(const uint8_t *buffer, size_t size) -{ - size_t i; - for (i=0; i - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include -#include - -namespace arduino -{ -namespace zephyr -{ - -int cbprintf_callback(int c, void *ctx); -size_t wrap_cbprintf(void *ctx, const char *format, ...); -size_t print_number_base_any(void *ctx, unsigned long long ull, int base); -size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits); - -template size_t print_number(void *ctx, Number n, const int base, const char *decfmt) -{ - if (base == 0) { - return reinterpret_cast(ctx)->write((char)n); - } else if (base == 2) { - return arduino::zephyr::print_number_base_pow2(ctx, n, 1); - } else if (base == 4) { - return arduino::zephyr::print_number_base_pow2(ctx, n, 2); - } else if (base == 8) { - return arduino::zephyr::print_number_base_pow2(ctx, n, 3); - } else if (base == 10) { - return arduino::zephyr::wrap_cbprintf(ctx, decfmt, n); - } else if (base == 16) { - return arduino::zephyr::print_number_base_pow2(ctx, n, 4); - } else if (base == 32) { - return arduino::zephyr::print_number_base_pow2(ctx, n, 5); - } else { - return arduino::zephyr::print_number_base_any(ctx, n, base); - } -} - -} // namespace zephyr - -} // namespace arduino - -inline size_t arduino::Print::print(const __FlashStringHelper *fsh) -{ - return write(reinterpret_cast(fsh)); -} - -inline size_t arduino::Print::print(const String &s) -{ - return write(s.c_str(), s.length()); -} - -inline size_t arduino::Print::print(const char str[]) -{ - return write(str); -} - -inline size_t arduino::Print::print(char c) -{ - return write(c); -} - -inline size_t arduino::Print::print(unsigned char n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%hhu"); -} - -inline size_t arduino::Print::print(int n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%d"); -} - -inline size_t arduino::Print::print(unsigned int n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%u"); -} - -inline size_t arduino::Print::print(long n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%ld"); -} - -inline size_t arduino::Print::print(unsigned long n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%lu"); -} - -inline size_t arduino::Print::print(long long n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%lld"); -} - -inline size_t arduino::Print::print(unsigned long long n, int base) -{ - return arduino::zephyr::print_number(this, n, base, "%llu"); -} - -inline size_t arduino::Print::print(double n, int perception) -{ - if (perception < 10) { - const char ch_perception = static_cast('0' + perception); - const char format[] = {'%', '.', ch_perception, 'f', '\0'}; - return arduino::zephyr::wrap_cbprintf(this, format, n); - } else { - const char ch_perception = static_cast('0' + (perception % 10)); - const char format[] = {'%', '.', '1', ch_perception, 'f', '\0'}; - return arduino::zephyr::wrap_cbprintf(this, format, n); - } -} - -inline size_t arduino::Print::print(const Printable &printable) -{ - return printable.printTo(*this); -} - -inline size_t arduino::Print::println(const __FlashStringHelper *fsh) -{ - return print(fsh) + println(); -} - -inline size_t arduino::Print::println(const String &s) -{ - return print(s) + println(); -} - -inline size_t arduino::Print::println(const char str[]) -{ - return print(str) + println(); -} - -inline size_t arduino::Print::println(char c) -{ - return print(c) + println(); -} - -inline size_t arduino::Print::println(unsigned char uc, int base) -{ - return print(uc, base) + println(); -} - -inline size_t arduino::Print::println(int i, int base) -{ - return print(i, base) + println(); -} - -inline size_t arduino::Print::println(unsigned int ui, int base) -{ - return print(ui, base) + println(); -} - -inline size_t arduino::Print::println(long l, int base) -{ - return print(l, base) + println(); -} - -inline size_t arduino::Print::println(unsigned long ul, int base) -{ - return print(ul, base) + println(); -} - -inline size_t arduino::Print::println(long long ll, int base) -{ - return print(ll, base) + println(); -} - -inline size_t arduino::Print::println(unsigned long long ull, int base) -{ - return print(ull, base) + println(); -} - -inline size_t arduino::Print::println(double d, int perception) -{ - return print(d, perception) + println(); -} - -inline size_t arduino::Print::println(const Printable &printable) -{ - return print(printable) + println(); -} - -inline size_t arduino::Print::println(void) -{ - return write("\r\n", 2); -} diff --git a/cores/arduino/zephyrSerial.cpp b/cores/arduino/zephyrSerial.cpp index d2568267..d2b44b51 100644 --- a/cores/arduino/zephyrSerial.cpp +++ b/cores/arduino/zephyrSerial.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace { @@ -65,6 +66,10 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) uart_configure(uart, &config); uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this); + k_sem_take(&rx.sem, K_FOREVER); + ring_buf_reset(&rx.ringbuf); + k_sem_give(&rx.sem); + uart_irq_rx_enable(uart); } @@ -78,10 +83,6 @@ void arduino::ZephyrSerial::IrqHandler() return; } - if (ring_buf_size_get(&tx.ringbuf) == 0) { - uart_irq_tx_disable(uart); - } - k_sem_take(&rx.sem, K_NO_WAIT); while (uart_irq_rx_ready(uart) && ((length = uart_fifo_read(uart, buf, sizeof(buf))) > 0)) { length = min(sizeof(buf), static_cast(length)); @@ -94,6 +95,11 @@ void arduino::ZephyrSerial::IrqHandler() k_sem_give(&rx.sem); k_sem_take(&tx.sem, K_NO_WAIT); + + if (ring_buf_size_get(&tx.ringbuf) == 0) { + uart_irq_tx_disable(uart); + } + while (uart_irq_tx_ready(uart) && ((length = ring_buf_size_get(&tx.ringbuf)) > 0)) { length = min(sizeof(buf), static_cast(length)); ring_buf_peek(&tx.ringbuf, &buf[0], length); @@ -110,6 +116,7 @@ void arduino::ZephyrSerial::IrqHandler() void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data) { + (void)dev; // unused reinterpret_cast(data)->IrqHandler(); } @@ -124,15 +131,26 @@ int arduino::ZephyrSerial::available() return ret; } +int arduino::ZephyrSerial::availableForWrite() +{ + int ret; + + k_sem_take(&rx.sem, K_FOREVER); + ret = ring_buf_space_get(&rx.ringbuf); + k_sem_give(&rx.sem); + + return ret; +} + int arduino::ZephyrSerial::peek() { uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_peek(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_peek(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret? data : -1; } int arduino::ZephyrSerial::read() @@ -140,31 +158,58 @@ int arduino::ZephyrSerial::read() uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_get(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_get(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret? data : -1; } size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size) { - int ret; - - k_sem_take(&tx.sem, K_FOREVER); - ret = ring_buf_put(&tx.ringbuf, buffer, size); - k_sem_give(&tx.sem); + size_t idx = 0; - if (ret < 0) { - return 0; + while (1) { + k_sem_take(&tx.sem, K_FOREVER); + auto ret = ring_buf_put(&tx.ringbuf, &buffer[idx], size-idx); + k_sem_give(&tx.sem); + if (ret < 0) { + return 0; + } + idx += ret; + if (ret == 0) { + uart_irq_tx_enable(uart); + yield(); + } + if (idx == size) { + break; + } } uart_irq_tx_enable(uart); - return ret; + return size; +} + +void arduino::ZephyrSerial::flush() { + while (ring_buf_size_get(&tx.ringbuf) > 0) { + k_yield(); + } + while (uart_irq_tx_complete(uart) == 0){ + k_yield(); + } } +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm)) +#define FIRST_UART_INDEX 1 +#else +#define FIRST_UART_INDEX 0 +#endif + #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) -arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, 0))); +#if !(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +// If CDC USB, use that object as Serial (and SerialUSB) +arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, FIRST_UART_INDEX))); +#endif #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) #define ARDUINO_SERIAL_DEFINED_0 1 diff --git a/cores/arduino/zephyrSerial.h b/cores/arduino/zephyrSerial.h index b62e54f6..ecc18a81 100644 --- a/cores/arduino/zephyrSerial.h +++ b/cores/arduino/zephyrSerial.h @@ -7,8 +7,7 @@ #pragma once #include - -#include +#include #include namespace arduino { @@ -16,8 +15,8 @@ namespace arduino { class ZephyrSerialStub : public HardwareSerial { public: - void begin(unsigned long baudRate) { } - void begin(unsigned long baudrate, uint16_t config) { } + void begin(__attribute__((unused)) unsigned long baudRate) { } + void begin(__attribute__((unused)) unsigned long baudrate, __attribute__((unused)) uint16_t config) { } void end() { } int available() { return 0; } int peek() { return 0; } @@ -38,7 +37,7 @@ class ZephyrSerial : public HardwareSerial template class ZephyrSerialBuffer { - friend arduino::ZephyrSerial; + friend arduino::ZephyrSerial; struct ring_buf ringbuf; uint8_t buffer[SZ]; struct k_sem sem; @@ -53,11 +52,13 @@ class ZephyrSerial : public HardwareSerial ZephyrSerial(const struct device *dev) : uart(dev) { } void begin(unsigned long baudrate, uint16_t config); void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); } - void flush() { } + void flush(); void end() { } size_t write(const uint8_t *buffer, size_t size); size_t write(const uint8_t data) { return write(&data, 1); } + using Print::write; // pull in write(str) and write(buf, size) from Print int available(); + int availableForWrite(); int peek(); int read(); @@ -66,6 +67,8 @@ class ZephyrSerial : public HardwareSerial return true; } + friend class SerialUSB_; + protected: void IrqHandler(); static void IrqDispatch(const struct device *dev, void *data); @@ -79,7 +82,10 @@ class ZephyrSerial : public HardwareSerial } // namespace arduino #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) +#if !(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +// If CDC USB, use that object as Serial (and SerialUSB) extern arduino::ZephyrSerial Serial; +#endif #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) #define SERIAL_DEFINED_0 1 #define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i; diff --git a/doc/zephyr_logo.jpg b/doc/zephyr_logo.jpg new file mode 100644 index 00000000..a17464be Binary files /dev/null and b/doc/zephyr_logo.jpg differ diff --git a/extra/bootstrap.sh b/extra/bootstrap.sh new file mode 100755 index 00000000..b074c971 --- /dev/null +++ b/extra/bootstrap.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -f platform.txt ]; then + echo Launch this script from the root core folder as ./extra/bootstrap.sh + exit 2 +fi + +NEEDED_HALS=$(grep 'build.zephyr_hals=' boards.txt | cut -d '=' -f 2 | xargs -n 1 echo | sort -u) + +HAL_FILTER="-hal_.*" +for hal in $NEEDED_HALS; do + HAL_FILTER="$HAL_FILTER,+$hal" +done + +python3 -m venv venv +source venv/bin/activate +pip install west +west init -l . +west config manifest.project-filter -- "$HAL_FILTER" +west update "$@" +west zephyr-export +pip install -r ../zephyr/scripts/requirements-base.txt +west sdk install --version 0.17.0 -t arm-zephyr-eabi + +for hal in $NEEDED_HALS; do + west blobs fetch $hal +done diff --git a/extra/build.sh b/extra/build.sh new file mode 100755 index 00000000..6d7d63f1 --- /dev/null +++ b/extra/build.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +set -e + +source venv/bin/activate + +ZEPHYR_BASE=$(west topdir)/zephyr + +if [ x$ZEPHYR_SDK_INSTALL_DIR == x"" ]; then + SDK_PATH=$(west sdk list | grep path | tail -n 1 | cut -d ':' -f 2 | tr -d ' ') + if [ x$SDK_PATH == x ]; then + echo "ZEPHYR_SDK_INSTALL_DIR not set and no SDK found" + exit 1 + fi + export ZEPHYR_SDK_INSTALL_DIR=${SDK_PATH} +fi + +if [ $# -eq 0 ] || [ x$1 == x"-h" ] || [ x$1 == x"--help" ]; then + cat << EOF +Usage: + $0 + $0 [] +Build the loader for the given target. + +When given an defined in 'boards.txt' (e.g. 'giga'), the actual +Zephyr board target and arguments are taken from that definition. + +When given a , it is passed as the '-b' argument to 'west build'. +Additional are passed as-is at the end of the command line. + +Available targets, as defined in 'boards.txt': + +EOF + extra/get_board_details.sh | + jq -r 'sort_by(.variant) | .[] | "\t\(.board)\t\(.target) \(.args)"' | + column -ts$'\t' + echo + exit 0 +fi + +# try to find the board in boards.txt +chosen_board=$(extra/get_board_details.sh | jq -cr ".[] | select(.board == \"$1\") // empty") +if ! [ -z "$chosen_board" ]; then + # found, use the target and args from there + target=$(jq -cr '.target' <<< "$chosen_board") + args=$(jq -cr '.args' <<< "$chosen_board") +else + # expect Zephyr-compatible target and args + target=$1 + shift + args="$*" +fi + +echo +echo "Build target: $target $args" + +# Get the variant name (NORMALIZED_BOARD_TARGET in Zephyr) +variant=$(extra/get_variant_name.sh $target) + +if [ -z "${variant}" ] ; then + echo "Failed to get variant name from '$target'" + exit 1 +else + echo "Build variant: $variant" +fi + +# Build the loader +BUILD_DIR=build/${variant} +VARIANT_DIR=variants/${variant} +rm -rf ${BUILD_DIR} +west build -d ${BUILD_DIR} -b ${target} loader -t llext-edk ${args} + +# Extract the generated EDK tarball and copy it to the variant directory +mkdir -p ${VARIANT_DIR} firmwares +(set -e ; cd ${BUILD_DIR} && rm -rf llext-edk && tar xf zephyr/llext-edk.tar.Z) +rsync -a --delete ${BUILD_DIR}/llext-edk ${VARIANT_DIR}/ + +# remove all inline comments in macro definitions +# (especially from devicetree_generated.h and sys/util_internal.h) +line_preproc_ok='^\s*#\s*(if|else|elif|endif)' # match conditional preproc lines +line_comment_only='^\s*\/\*' # match lines starting with comment +line_continuation='\\$' # match lines ending with '\' +c_comment='\s*\/\*.*?\*\/' # match C-style comments and any preceding space +perl -i -pe "s/${c_comment}//gs unless /${line_preproc_ok}/ || (/${line_comment_only}/ && !/${line_continuation}/)" $(find ${VARIANT_DIR}/llext-edk/include/ -type f) +for ext in elf bin hex; do + rm -f firmwares/zephyr-$variant.$ext + if [ -f ${BUILD_DIR}/zephyr/zephyr.$ext ]; then + cp ${BUILD_DIR}/zephyr/zephyr.$ext firmwares/zephyr-$variant.$ext + fi +done + +# Generate the provides.ld file for linked builds +echo "Exporting provides.ld" +READELF=${ZEPHYR_SDK_INSTALL_DIR}/arm-zephyr-eabi/bin/arm-zephyr-eabi-readelf +$READELF --wide -s ${BUILD_DIR}/zephyr/zephyr.elf | c++filt | grep FUNC | awk -F' ' '{print "PROVIDE("$8" = 0x"$2");"}' > ${VARIANT_DIR}/provides.ld +$READELF --wide -s ${BUILD_DIR}/zephyr/zephyr.elf | c++filt | grep kheap_llext_heap | awk -F' ' '{print "PROVIDE("$8" = 0x"$2");"}' >> ${VARIANT_DIR}/provides.ld +$READELF --wide -s ${BUILD_DIR}/zephyr/zephyr.elf | c++filt | grep kheap_llext_heap | awk -F' ' '{print "PROVIDE(kheap_llext_heap_size = "$3");"}' >> ${VARIANT_DIR}/provides.ld +$READELF --wide -s ${BUILD_DIR}/zephyr/zephyr.elf | c++filt | grep kheap__system_heap | awk -F' ' '{print "PROVIDE("$8" = 0x"$2");"}' >> ${VARIANT_DIR}/provides.ld +$READELF --wide -s ${BUILD_DIR}/zephyr/zephyr.elf | c++filt | grep kheap__system_heap | awk -F' ' '{print "PROVIDE(kheap__system_heap_size = "$3");"}' >> ${VARIANT_DIR}/provides.ld +cat ${BUILD_DIR}/zephyr/zephyr.map | grep __device_dts_ord | grep -v rodata | grep -v llext_const_symbol | awk -F' ' '{print "PROVIDE("$2" = "$1");"}' >> ${VARIANT_DIR}/provides.ld +TEXT_START=`cat variants/$variant/$variant.overlay | grep user_sketch: | cut -f2 -d"@" | cut -f1 -d"{"` +echo "PROVIDE(_sketch_start = 0x$TEXT_START);" >> ${VARIANT_DIR}/provides.ld + +sed -i 's/PROVIDE(malloc =/PROVIDE(__wrap_malloc =/g' ${VARIANT_DIR}/provides.ld +sed -i 's/PROVIDE(free =/PROVIDE(__wrap_free =/g' ${VARIANT_DIR}/provides.ld +sed -i 's/PROVIDE(realloc =/PROVIDE(__wrap_realloc =/g' ${VARIANT_DIR}/provides.ld +sed -i 's/PROVIDE(calloc =/PROVIDE(__wrap_calloc =/g' ${VARIANT_DIR}/provides.ld +sed -i 's/PROVIDE(random =/PROVIDE(__wrap_random =/g' ${VARIANT_DIR}/provides.ld + +cmake -P extra/gen_arduino_files.cmake $variant diff --git a/extra/build_all.sh b/extra/build_all.sh new file mode 100755 index 00000000..f132b83d --- /dev/null +++ b/extra/build_all.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +FORCE=false + +while getopts "hf" opt; do + case $opt in + h) + echo "Usage: $0 [-hfl]" + echo " -h Show this help message" + echo " -f Force build all targets" + exit 0 + ;; + f) + FORCE=true + ;; + *) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +if [ ! -z "$GITHUB_STEP_SUMMARY" ] ; then + echo "### Variant build results:" >> "$GITHUB_STEP_SUMMARY" +fi + +final_result=0 +while read -r item; do + board=$(jq -cr '.board' <<< "$item") + variant=$(jq -cr '.variant' <<< "$item") + target=$(jq -cr '.target' <<< "$item") + args=$(jq -cr '.args // ""' <<< "$item") + + if [ -z "$GITHUB_STEP_SUMMARY" ] ; then + echo && echo + echo "${board} (${variant})" + echo "${board} (${variant})" | sed -e 's/./=/g' + else + echo "::group::=== ${board} (${variant}) ===" + fi + + ./extra/build.sh "$target" $args + result=$? + final_result=$((final_result | result)) + + if [ -z "$GITHUB_STEP_SUMMARY" ] ; then + echo + echo "${variant} result: $result" + else + echo "::endgroup::" + if [ $result -eq 0 ] ; then + echo "- :white_check_mark: \`${variant}\`" >> "$GITHUB_STEP_SUMMARY" + else + echo "^^$(echo "=== ${board} (${variant}) ===" | sed -e 's/./^/g') FAILED with $result!" + echo "- :x: \`${variant}\`" >> "$GITHUB_STEP_SUMMARY" + fi + fi + [ $result -ne 0 ] && ! $FORCE && exit $result +done < <(extra/get_board_details.sh | jq -cr 'sort_by(.variant) | .[]') + +exit $final_result diff --git a/extra/gen_arduino_files.cmake b/extra/gen_arduino_files.cmake new file mode 100644 index 00000000..e4e859fa --- /dev/null +++ b/extra/gen_arduino_files.cmake @@ -0,0 +1,61 @@ +# get root dir for the project +cmake_path(SET TOP_DIR NORMALIZE ${CMAKE_CURRENT_LIST_DIR}/..) + +# get list of variants to be applied +if(CMAKE_ARGC GREATER 3) + # cmake -P